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:
Mark Olesen
2017-11-23 20:17:33 +01:00
parent 6e8586df5d
commit d49929b210
5 changed files with 170 additions and 42 deletions

View File

@ -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;

View File

@ -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);
} }
} }

View File

@ -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.

View File

@ -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;

View File

@ -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