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