diff --git a/applications/test/string/Test-string.C b/applications/test/string/Test-string.C index 73c703d170..60cd2a83f5 100644 --- a/applications/test/string/Test-string.C +++ b/applications/test/string/Test-string.C @@ -62,6 +62,18 @@ int main(int argc, char *argv[]) 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:" << unsigned(string::hash()(test)) << endl; diff --git a/applications/test/stringSplit/Test-stringSplit.C b/applications/test/stringSplit/Test-stringSplit.C index 3d0c820d24..fa91d81f5e 100644 --- a/applications/test/stringSplit/Test-stringSplit.C +++ b/applications/test/stringSplit/Test-stringSplit.C @@ -71,6 +71,18 @@ int main(int argc, char *argv[]) "string", "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 ( "slash", @@ -81,6 +93,11 @@ int main(int argc, char *argv[]) "space", "test split on space" ); + argList::addBoolOption + ( + "empty", + "preserve empty strings in split" + ); argList args(argc, argv, false, true); if (args.size() <= 1 && args.options().empty()) @@ -88,8 +105,10 @@ int main(int argc, char *argv[]) args.printUsage(); } + const bool keepEmpty = args.optionFound("empty"); + int nopts = 0; - for (auto optName : { "any", "slash", "space", "sub" }) + for (auto optName : { "any", "slash", "space", "sub", "fixed", "char" }) { 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 if (!nopts || args.optionFound("slash")) { + const char delim = '/'; + Info<< "split on slash" << nl << "~~~~~~~~~~~~~~" << nl; 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); } } diff --git a/src/OpenFOAM/primitives/strings/stringOps/stringOps.H b/src/OpenFOAM/primitives/strings/stringOps/stringOps.H index de89b954a7..609aff6e9b 100644 --- a/src/OpenFOAM/primitives/strings/stringOps/stringOps.H +++ b/src/OpenFOAM/primitives/strings/stringOps/stringOps.H @@ -335,26 +335,29 @@ namespace stringOps //- 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 Foam::SubStrings split ( const StringType& str, - const char delim + const char delim, + const bool keepEmpty = false ); //- Split string into sub-strings using delimiter string. - // Empty sub-strings are suppressed. + // Empty sub-strings are normally suppressed. template Foam::SubStrings split ( 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. - // Empty sub-strings are suppressed. + // Empty sub-strings are normally suppressed. + // Behaviour is ill-defined if delim is an empty string. template Foam::SubStrings splitAny ( @@ -362,6 +365,14 @@ namespace stringOps const std::string& delim ); + //- Split string into sub-strings using a fixed field width + // Behaviour is ill-defined if width is zero. + template + Foam::SubStrings splitFixed + ( + const StringType& str, + const std::string::size_type width + ); //- Split string into sub-strings at whitespace (TAB, NL, VT, FF, CR, SPC) // Empty sub-strings are suppressed. diff --git a/src/OpenFOAM/primitives/strings/stringOps/stringOpsTemplates.C b/src/OpenFOAM/primitives/strings/stringOps/stringOpsTemplates.C index dc9f771de8..53c72fa2b5 100644 --- a/src/OpenFOAM/primitives/strings/stringOps/stringOpsTemplates.C +++ b/src/OpenFOAM/primitives/strings/stringOps/stringOpsTemplates.C @@ -37,20 +37,21 @@ Foam::word Foam::stringOps::name const PrimitiveType& val ) { - // same concept as GNU/BSD asprintf() - // use snprintf with zero to determine the number of characters required + word output; - 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) { - char buf[n+1]; - ::snprintf(buf, n+1, fmt, val); - buf[n] = 0; + output.resize(n+1); + char* buf = &(output[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 Foam::SubStrings Foam::stringOps::split ( const StringType& str, - const char delim + const char delim, + const bool keepEmpty ) { Foam::SubStrings lst; + if (str.empty() || !delim) + { + return lst; + } + lst.reserve(20); std::string::size_type beg = 0, end = 0; - 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); } beg = end + 1; } - // (Non-empty) trailing element - if (beg < str.size()) + // Trailing element + 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; @@ -101,28 +106,32 @@ template Foam::SubStrings Foam::stringOps::split ( const StringType& str, - const std::string& delim + const std::string& delim, + const bool keepEmpty ) { Foam::SubStrings lst; + if (str.empty() || delim.empty()) + { + return lst; + } + lst.reserve(20); std::string::size_type beg = 0, end = 0; - 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); } beg = end + delim.size(); } - // (Non-empty) trailing element - if (beg < str.size()) + // Trailing element + 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; @@ -137,30 +146,67 @@ Foam::SubStrings Foam::stringOps::splitAny ) { Foam::SubStrings lst; + if (str.empty() || delim.empty()) + { + return lst; + } + lst.reserve(20); - std::string::size_type beg = 0; - - while + for ( - (beg = str.find_first_not_of(delim, beg)) - != std::string::npos + std::string::size_type pos = 0; + (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) { // Trailing element - lst.append(str.cbegin() + beg, str.cbegin() + str.size()); + lst.append(str.cbegin() + pos, str.cend()); break; } - else + + // Intermediate element + lst.append(str.cbegin() + pos, str.cbegin() + end); + + pos = end + 1; + } + + return lst; +} + + +template +Foam::SubStrings Foam::stringOps::splitFixed +( + const StringType& str, + const std::string::size_type width +) +{ + Foam::SubStrings 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) { - // Intermediate element - lst.append(str.cbegin() + beg, str.cbegin() + end); - beg = end + 1; + // Trailing element + lst.append(str.cbegin() + pos, str.cend()); + break; } + + lst.append(str.cbegin() + pos, str.cbegin() + end); } return lst; diff --git a/src/fileFormats/ensight/file/ensightCaseOptions.C b/src/fileFormats/ensight/file/ensightCaseOptions.C index 7977334466..d95b4bd656 100644 --- a/src/fileFormats/ensight/file/ensightCaseOptions.C +++ b/src/fileFormats/ensight/file/ensightCaseOptions.C @@ -59,7 +59,7 @@ Foam::word Foam::ensightCase::options::padded(const label i) const // As per Foam::name, but with fixed length char buf[32]; - ::snprintf(buf, 32, printf_.c_str(), i); + ::snprintf(buf, 32, printf_.c_str(), static_cast(i)); buf[31] = 0; // no stripping required