From fbdd16a2932406e6dac24dd38339913cd6695e38 Mon Sep 17 00:00:00 2001 From: Mark Olesen <Mark.Olesen@esi-group.com> Date: Mon, 14 Aug 2017 10:36:12 +0200 Subject: [PATCH] ENH: add stringOps::splitAny, stringOps::splitSpace - assists when building simple hand-rolled parsers. Also add string::split() taking a sub-string for the delimiter. --- .../test/stringSplit/Test-stringSplit.C | 105 ++++++++++++++++-- .../primitives/strings/stringOps/stringOps.H | 35 +++++- .../strings/stringOps/stringOpsTemplates.C | 84 +++++++++++++- 3 files changed, 212 insertions(+), 12 deletions(-) diff --git a/applications/test/stringSplit/Test-stringSplit.C b/applications/test/stringSplit/Test-stringSplit.C index 9b1208c252f..3d0c820d24e 100644 --- a/applications/test/stringSplit/Test-stringSplit.C +++ b/applications/test/stringSplit/Test-stringSplit.C @@ -35,11 +35,10 @@ Description using namespace Foam; +// Simple utility template<class String> -void printSplitting(const String& str, const char delimiter) +void printSubStrings(const String& str, const SubStrings<String>& split) { - auto split = stringOps::split(str, delimiter); - Info<< "string {" << str.size() << " chars} = " << str << nl << split.size() << " elements {" << split.length() << " chars}" << nl; @@ -60,7 +59,28 @@ int main(int argc, char *argv[]) { argList::noBanner(); argList::noParallel(); - + argList::addOption + ( + "any", + "delimChars", + "test split on any delimiter characters" + ); + argList::addOption + ( + "sub", + "string", + "test split on substring" + ); + argList::addBoolOption + ( + "slash", + "test split on slash (default)" + ); + argList::addBoolOption + ( + "space", + "test split on space" + ); argList args(argc, argv, false, true); if (args.size() <= 1 && args.options().empty()) @@ -68,12 +88,83 @@ int main(int argc, char *argv[]) args.printUsage(); } - for (label argi=1; argi < args.size(); ++argi) + int nopts = 0; + for (auto optName : { "any", "slash", "space", "sub" }) + { + if (args.optionFound(optName)) + { + ++nopts; + } + } + + if (args.optionFound("any")) + { + const std::string& str = args["any"]; + Info<< "split on any chars" << nl + << "=" << str << nl + << "~~~~~~~~~~~~~~~" << nl; + + for (label argi=1; argi < args.size(); ++argi) + { + const auto split = stringOps::splitAny(args[argi], str); + printSubStrings(args[argi], split); + } + + if (nopts == 1) + { + return 0; + } + } + + if (args.optionFound("sub")) + { + const std::string& str = args["sub"]; + Info<< "split on substring" << nl + << "=" << str << nl + << "~~~~~~~~~~~~~~~" << nl; + + for (label argi=1; argi < args.size(); ++argi) + { + const auto split = stringOps::split(args[argi], str); + printSubStrings(args[argi], split); + } + + if (nopts == 1) + { + return 0; + } + } + + if (args.optionFound("space")) + { + Info<< "split on space" << nl + << "~~~~~~~~~~~~~~" << nl; + + for (label argi=1; argi < args.size(); ++argi) + { + const auto split = stringOps::splitSpace(args[argi]); + printSubStrings(args[argi], split); + } + + if (nopts == 1) + { + return 0; + } + } + + // Default + if (!nopts || args.optionFound("slash")) { - printSplitting(args[argi], '/'); + Info<< "split on slash" << nl + << "~~~~~~~~~~~~~~" << nl; + + for (label argi=1; argi < args.size(); ++argi) + { + const auto split = stringOps::split(args[argi], '/'); + printSubStrings(args[argi], split); + } } - Info<< "\nEnd\n" << endl; return 0; } diff --git a/src/OpenFOAM/primitives/strings/stringOps/stringOps.H b/src/OpenFOAM/primitives/strings/stringOps/stringOps.H index 0d7d459f596..92092f9d520 100644 --- a/src/OpenFOAM/primitives/strings/stringOps/stringOps.H +++ b/src/OpenFOAM/primitives/strings/stringOps/stringOps.H @@ -304,15 +304,44 @@ namespace stringOps Foam::word name(const std::string& fmt, const PrimitiveType& val); - //- Split a string into sub-strings at the delimiter character. - // An empty sub-strings are suppressed. + //- Split string into sub-strings at the delimiter character. + // Empty sub-strings are suppressed. template<class StringType> Foam::SubStrings<StringType> split ( const StringType& str, - const char delimiter + const char delim ); + //- Split string into sub-strings using delimiter string. + // Empty sub-strings are suppressed. + template<class StringType> + Foam::SubStrings<StringType> split + ( + const StringType& str, + const std::string& delim + ); + + + //- Split string into sub-strings using any characters in delimiter. + // Empty sub-strings are suppressed. + template<class StringType> + Foam::SubStrings<StringType> splitAny + ( + const StringType& str, + const std::string& delim + ); + + + //- Split string into sub-strings at whitespace (TAB, NL, VT, FF, CR, SPC) + // Empty sub-strings are suppressed. + template<class StringType> + Foam::SubStrings<StringType> splitSpace + ( + const StringType& str + ); + + } // End namespace stringOps // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // diff --git a/src/OpenFOAM/primitives/strings/stringOps/stringOpsTemplates.C b/src/OpenFOAM/primitives/strings/stringOps/stringOpsTemplates.C index 1b0aa62e9fd..dc9f771de88 100644 --- a/src/OpenFOAM/primitives/strings/stringOps/stringOpsTemplates.C +++ b/src/OpenFOAM/primitives/strings/stringOps/stringOpsTemplates.C @@ -69,7 +69,7 @@ template<class StringType> Foam::SubStrings<StringType> Foam::stringOps::split ( const StringType& str, - const char delimiter + const char delim ) { Foam::SubStrings<StringType> lst; @@ -77,7 +77,7 @@ Foam::SubStrings<StringType> Foam::stringOps::split std::string::size_type beg = 0, end = 0; - while ((end = str.find(delimiter, beg)) != std::string::npos) + while ((end = str.find(delim, beg)) != std::string::npos) { if (beg < end) { @@ -97,4 +97,84 @@ Foam::SubStrings<StringType> Foam::stringOps::split } +template<class StringType> +Foam::SubStrings<StringType> Foam::stringOps::split +( + const StringType& str, + const std::string& delim +) +{ + Foam::SubStrings<StringType> lst; + lst.reserve(20); + + std::string::size_type beg = 0, end = 0; + + while ((end = str.find(delim, beg)) != std::string::npos) + { + if (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()) + { + lst.append(str.cbegin() + beg, str.cbegin() + str.size()); + } + + return lst; +} + + +template<class StringType> +Foam::SubStrings<StringType> Foam::stringOps::splitAny +( + const StringType& str, + const std::string& delim +) +{ + Foam::SubStrings<StringType> lst; + lst.reserve(20); + + std::string::size_type beg = 0; + + while + ( + (beg = str.find_first_not_of(delim, beg)) + != std::string::npos + ) + { + const auto end = str.find_first_of(delim, beg); + + if (end == std::string::npos) + { + // Trailing element + lst.append(str.cbegin() + beg, str.cbegin() + str.size()); + break; + } + else + { + // Intermediate element + lst.append(str.cbegin() + beg, str.cbegin() + end); + beg = end + 1; + } + } + + return lst; +} + + +template<class StringType> +Foam::SubStrings<StringType> Foam::stringOps::splitSpace +( + const StringType& str +) +{ + return splitAny(str, "\t\n\v\f\r "); +} + + // ************************************************************************* // -- GitLab