mirror of
https://develop.openfoam.com/Development/openfoam.git
synced 2025-11-28 03:28:01 +00:00
ENH: improvements to stringOps format and split functions
- split now optionally retains empty substrings. Added split on fixed field width. - Foam::name() now formats directly into string buffer, which a removes one layer of copying and also avoids using a non-constexpr in the temporary. STYLE: explicit type narrowing on zero-padded output for ensight
This commit is contained in:
@ -62,6 +62,18 @@ int main(int argc, char *argv[])
|
|||||||
dict.add("FOAM_RUN", subDict);
|
dict.add("FOAM_RUN", subDict);
|
||||||
|
|
||||||
|
|
||||||
|
// Test Foam::name with formatting string
|
||||||
|
{
|
||||||
|
word formatted = Foam::name("formatted=<%X>", 0xdeadbeef);
|
||||||
|
Info<<"formatted: " << formatted << nl;
|
||||||
|
}
|
||||||
|
|
||||||
|
Info<<"formatted: "
|
||||||
|
<< Foam::name("formatted not checked for validity=<%X>", 0xdeadbeef)
|
||||||
|
<< nl
|
||||||
|
<< endl
|
||||||
|
|
||||||
|
|
||||||
Info<< "string:" << test << nl << "hash:"
|
Info<< "string:" << test << nl << "hash:"
|
||||||
<< unsigned(string::hash()(test)) << endl;
|
<< unsigned(string::hash()(test)) << endl;
|
||||||
|
|
||||||
|
|||||||
@ -71,6 +71,18 @@ int main(int argc, char *argv[])
|
|||||||
"string",
|
"string",
|
||||||
"test split on substring"
|
"test split on substring"
|
||||||
);
|
);
|
||||||
|
argList::addOption
|
||||||
|
(
|
||||||
|
"char",
|
||||||
|
"delim",
|
||||||
|
"test split on specified delimiter character"
|
||||||
|
);
|
||||||
|
argList::addOption
|
||||||
|
(
|
||||||
|
"fixed",
|
||||||
|
"int",
|
||||||
|
"test split on fixed width"
|
||||||
|
);
|
||||||
argList::addBoolOption
|
argList::addBoolOption
|
||||||
(
|
(
|
||||||
"slash",
|
"slash",
|
||||||
@ -81,6 +93,11 @@ int main(int argc, char *argv[])
|
|||||||
"space",
|
"space",
|
||||||
"test split on space"
|
"test split on space"
|
||||||
);
|
);
|
||||||
|
argList::addBoolOption
|
||||||
|
(
|
||||||
|
"empty",
|
||||||
|
"preserve empty strings in split"
|
||||||
|
);
|
||||||
argList args(argc, argv, false, true);
|
argList args(argc, argv, false, true);
|
||||||
|
|
||||||
if (args.size() <= 1 && args.options().empty())
|
if (args.size() <= 1 && args.options().empty())
|
||||||
@ -88,8 +105,10 @@ int main(int argc, char *argv[])
|
|||||||
args.printUsage();
|
args.printUsage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const bool keepEmpty = args.optionFound("empty");
|
||||||
|
|
||||||
int nopts = 0;
|
int nopts = 0;
|
||||||
for (auto optName : { "any", "slash", "space", "sub" })
|
for (auto optName : { "any", "slash", "space", "sub", "fixed", "char" })
|
||||||
{
|
{
|
||||||
if (args.optionFound(optName))
|
if (args.optionFound(optName))
|
||||||
{
|
{
|
||||||
@ -152,15 +171,55 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (args.optionFound("char"))
|
||||||
|
{
|
||||||
|
const char delim = args["char"][0];
|
||||||
|
|
||||||
|
Info<< "split on char=" << delim << nl
|
||||||
|
<< "~~~~~~~~~~~~~~" << nl;
|
||||||
|
|
||||||
|
for (label argi=1; argi < args.size(); ++argi)
|
||||||
|
{
|
||||||
|
const auto split = stringOps::split(args[argi], delim, keepEmpty);
|
||||||
|
printSubStrings(args[argi], split);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nopts == 1)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.optionFound("fixed"))
|
||||||
|
{
|
||||||
|
const label width = readLabel(args["fixed"]);
|
||||||
|
|
||||||
|
Info<< "split on fixed width = " << width << nl
|
||||||
|
<< "~~~~~~~~~~~~~~" << nl;
|
||||||
|
|
||||||
|
for (label argi=1; argi < args.size(); ++argi)
|
||||||
|
{
|
||||||
|
const auto split = stringOps::splitFixed(args[argi], width);
|
||||||
|
printSubStrings(args[argi], split);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nopts == 1)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Default
|
// Default
|
||||||
if (!nopts || args.optionFound("slash"))
|
if (!nopts || args.optionFound("slash"))
|
||||||
{
|
{
|
||||||
|
const char delim = '/';
|
||||||
|
|
||||||
Info<< "split on slash" << nl
|
Info<< "split on slash" << nl
|
||||||
<< "~~~~~~~~~~~~~~" << nl;
|
<< "~~~~~~~~~~~~~~" << nl;
|
||||||
|
|
||||||
for (label argi=1; argi < args.size(); ++argi)
|
for (label argi=1; argi < args.size(); ++argi)
|
||||||
{
|
{
|
||||||
const auto split = stringOps::split(args[argi], '/');
|
const auto split = stringOps::split(args[argi], delim, keepEmpty);
|
||||||
printSubStrings(args[argi], split);
|
printSubStrings(args[argi], split);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -335,26 +335,29 @@ namespace stringOps
|
|||||||
|
|
||||||
|
|
||||||
//- Split string into sub-strings at the delimiter character.
|
//- Split string into sub-strings at the delimiter character.
|
||||||
// Empty sub-strings are suppressed.
|
// Empty sub-strings are normally suppressed.
|
||||||
|
// Behaviour is ill-defined if delim is a NUL character.
|
||||||
template<class StringType>
|
template<class StringType>
|
||||||
Foam::SubStrings<StringType> split
|
Foam::SubStrings<StringType> split
|
||||||
(
|
(
|
||||||
const StringType& str,
|
const StringType& str,
|
||||||
const char delim
|
const char delim,
|
||||||
|
const bool keepEmpty = false
|
||||||
);
|
);
|
||||||
|
|
||||||
//- Split string into sub-strings using delimiter string.
|
//- Split string into sub-strings using delimiter string.
|
||||||
// Empty sub-strings are suppressed.
|
// Empty sub-strings are normally suppressed.
|
||||||
template<class StringType>
|
template<class StringType>
|
||||||
Foam::SubStrings<StringType> split
|
Foam::SubStrings<StringType> split
|
||||||
(
|
(
|
||||||
const StringType& str,
|
const StringType& str,
|
||||||
const std::string& delim
|
const std::string& delim,
|
||||||
|
const bool keepEmpty = false
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
//- Split string into sub-strings using any characters in delimiter.
|
//- Split string into sub-strings using any characters in delimiter.
|
||||||
// Empty sub-strings are suppressed.
|
// Empty sub-strings are normally suppressed.
|
||||||
|
// Behaviour is ill-defined if delim is an empty string.
|
||||||
template<class StringType>
|
template<class StringType>
|
||||||
Foam::SubStrings<StringType> splitAny
|
Foam::SubStrings<StringType> splitAny
|
||||||
(
|
(
|
||||||
@ -362,6 +365,14 @@ namespace stringOps
|
|||||||
const std::string& delim
|
const std::string& delim
|
||||||
);
|
);
|
||||||
|
|
||||||
|
//- Split string into sub-strings using a fixed field width
|
||||||
|
// Behaviour is ill-defined if width is zero.
|
||||||
|
template<class StringType>
|
||||||
|
Foam::SubStrings<StringType> splitFixed
|
||||||
|
(
|
||||||
|
const StringType& str,
|
||||||
|
const std::string::size_type width
|
||||||
|
);
|
||||||
|
|
||||||
//- Split string into sub-strings at whitespace (TAB, NL, VT, FF, CR, SPC)
|
//- Split string into sub-strings at whitespace (TAB, NL, VT, FF, CR, SPC)
|
||||||
// Empty sub-strings are suppressed.
|
// Empty sub-strings are suppressed.
|
||||||
|
|||||||
@ -37,20 +37,21 @@ Foam::word Foam::stringOps::name
|
|||||||
const PrimitiveType& val
|
const PrimitiveType& val
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
// same concept as GNU/BSD asprintf()
|
word output;
|
||||||
// use snprintf with zero to determine the number of characters required
|
|
||||||
|
|
||||||
const int n = ::snprintf(nullptr, 0, fmt, val);
|
// snprintf with zero to find size (without '\0') required
|
||||||
|
int n = ::snprintf(nullptr, 0, fmt, val);
|
||||||
if (n > 0)
|
if (n > 0)
|
||||||
{
|
{
|
||||||
char buf[n+1];
|
output.resize(n+1);
|
||||||
::snprintf(buf, n+1, fmt, val);
|
char* buf = &(output[0]);
|
||||||
buf[n] = 0;
|
|
||||||
|
|
||||||
return word(buf, false); // no stripping desired
|
// Print directly into buffer, no stripping desired
|
||||||
|
n = ::snprintf(buf, n+1, fmt, val);
|
||||||
|
output.resize(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
return word::null;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -69,28 +70,32 @@ template<class StringType>
|
|||||||
Foam::SubStrings<StringType> Foam::stringOps::split
|
Foam::SubStrings<StringType> Foam::stringOps::split
|
||||||
(
|
(
|
||||||
const StringType& str,
|
const StringType& str,
|
||||||
const char delim
|
const char delim,
|
||||||
|
const bool keepEmpty
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
Foam::SubStrings<StringType> lst;
|
Foam::SubStrings<StringType> lst;
|
||||||
|
if (str.empty() || !delim)
|
||||||
|
{
|
||||||
|
return lst;
|
||||||
|
}
|
||||||
|
|
||||||
lst.reserve(20);
|
lst.reserve(20);
|
||||||
|
|
||||||
std::string::size_type beg = 0, end = 0;
|
std::string::size_type beg = 0, end = 0;
|
||||||
|
|
||||||
while ((end = str.find(delim, beg)) != std::string::npos)
|
while ((end = str.find(delim, beg)) != std::string::npos)
|
||||||
{
|
{
|
||||||
if (beg < end)
|
if (keepEmpty || (beg < end))
|
||||||
{
|
{
|
||||||
// (Non-empty) intermediate element
|
|
||||||
lst.append(str.cbegin() + beg, str.cbegin() + end);
|
lst.append(str.cbegin() + beg, str.cbegin() + end);
|
||||||
}
|
}
|
||||||
beg = end + 1;
|
beg = end + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// (Non-empty) trailing element
|
// Trailing element
|
||||||
if (beg < str.size())
|
if (keepEmpty ? (beg == str.size()) : (beg < str.size()))
|
||||||
{
|
{
|
||||||
lst.append(str.cbegin() + beg, str.cbegin() + str.size());
|
lst.append(str.cbegin() + beg, str.cend());
|
||||||
}
|
}
|
||||||
|
|
||||||
return lst;
|
return lst;
|
||||||
@ -101,28 +106,32 @@ template<class StringType>
|
|||||||
Foam::SubStrings<StringType> Foam::stringOps::split
|
Foam::SubStrings<StringType> Foam::stringOps::split
|
||||||
(
|
(
|
||||||
const StringType& str,
|
const StringType& str,
|
||||||
const std::string& delim
|
const std::string& delim,
|
||||||
|
const bool keepEmpty
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
Foam::SubStrings<StringType> lst;
|
Foam::SubStrings<StringType> lst;
|
||||||
|
if (str.empty() || delim.empty())
|
||||||
|
{
|
||||||
|
return lst;
|
||||||
|
}
|
||||||
|
|
||||||
lst.reserve(20);
|
lst.reserve(20);
|
||||||
|
|
||||||
std::string::size_type beg = 0, end = 0;
|
std::string::size_type beg = 0, end = 0;
|
||||||
|
|
||||||
while ((end = str.find(delim, beg)) != std::string::npos)
|
while ((end = str.find(delim, beg)) != std::string::npos)
|
||||||
{
|
{
|
||||||
if (beg < end)
|
if (keepEmpty || (beg < end))
|
||||||
{
|
{
|
||||||
// (Non-empty) intermediate element
|
|
||||||
lst.append(str.cbegin() + beg, str.cbegin() + end);
|
lst.append(str.cbegin() + beg, str.cbegin() + end);
|
||||||
}
|
}
|
||||||
beg = end + delim.size();
|
beg = end + delim.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
// (Non-empty) trailing element
|
// Trailing element
|
||||||
if (beg < str.size())
|
if (keepEmpty ? (beg == str.size()) : (beg < str.size()))
|
||||||
{
|
{
|
||||||
lst.append(str.cbegin() + beg, str.cbegin() + str.size());
|
lst.append(str.cbegin() + beg, str.cend());
|
||||||
}
|
}
|
||||||
|
|
||||||
return lst;
|
return lst;
|
||||||
@ -137,30 +146,67 @@ Foam::SubStrings<StringType> Foam::stringOps::splitAny
|
|||||||
)
|
)
|
||||||
{
|
{
|
||||||
Foam::SubStrings<StringType> lst;
|
Foam::SubStrings<StringType> lst;
|
||||||
|
if (str.empty() || delim.empty())
|
||||||
|
{
|
||||||
|
return lst;
|
||||||
|
}
|
||||||
|
|
||||||
lst.reserve(20);
|
lst.reserve(20);
|
||||||
|
|
||||||
std::string::size_type beg = 0;
|
for
|
||||||
|
|
||||||
while
|
|
||||||
(
|
(
|
||||||
(beg = str.find_first_not_of(delim, beg))
|
std::string::size_type pos = 0;
|
||||||
!= std::string::npos
|
(pos = str.find_first_not_of(delim, pos)) != std::string::npos;
|
||||||
|
/*nil*/
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
const auto end = str.find_first_of(delim, beg);
|
const auto end = str.find_first_of(delim, pos);
|
||||||
|
|
||||||
if (end == std::string::npos)
|
if (end == std::string::npos)
|
||||||
{
|
{
|
||||||
// Trailing element
|
// Trailing element
|
||||||
lst.append(str.cbegin() + beg, str.cbegin() + str.size());
|
lst.append(str.cbegin() + pos, str.cend());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
// Intermediate element
|
// Intermediate element
|
||||||
lst.append(str.cbegin() + beg, str.cbegin() + end);
|
lst.append(str.cbegin() + pos, str.cbegin() + end);
|
||||||
beg = end + 1;
|
|
||||||
|
pos = end + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return lst;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<class StringType>
|
||||||
|
Foam::SubStrings<StringType> Foam::stringOps::splitFixed
|
||||||
|
(
|
||||||
|
const StringType& str,
|
||||||
|
const std::string::size_type width
|
||||||
|
)
|
||||||
|
{
|
||||||
|
Foam::SubStrings<StringType> lst;
|
||||||
|
if (str.empty() || !width)
|
||||||
|
{
|
||||||
|
return lst;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto len = str.size();
|
||||||
|
lst.reserve(1 + (len / width));
|
||||||
|
|
||||||
|
for (std::string::size_type pos = 0; pos < len; pos += width)
|
||||||
|
{
|
||||||
|
const auto end = (pos + width);
|
||||||
|
|
||||||
|
if (end >= len)
|
||||||
|
{
|
||||||
|
// Trailing element
|
||||||
|
lst.append(str.cbegin() + pos, str.cend());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
lst.append(str.cbegin() + pos, str.cbegin() + end);
|
||||||
}
|
}
|
||||||
|
|
||||||
return lst;
|
return lst;
|
||||||
|
|||||||
@ -59,7 +59,7 @@ Foam::word Foam::ensightCase::options::padded(const label i) const
|
|||||||
// As per Foam::name, but with fixed length
|
// As per Foam::name, but with fixed length
|
||||||
char buf[32];
|
char buf[32];
|
||||||
|
|
||||||
::snprintf(buf, 32, printf_.c_str(), i);
|
::snprintf(buf, 32, printf_.c_str(), static_cast<int>(i));
|
||||||
buf[31] = 0;
|
buf[31] = 0;
|
||||||
|
|
||||||
// no stripping required
|
// no stripping required
|
||||||
|
|||||||
Reference in New Issue
Block a user