diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index b880a7a9fd..890b2f997e 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -2396,6 +2396,19 @@ BOOST_AUTO_TEST_CASE(test_SplitString) BOOST_CHECK_EQUAL(result.size(), 1); BOOST_CHECK_EQUAL(result[0], "AAA"); } + + // multiple split characters + { + using V = std::vector; + BOOST_TEST(SplitString("a,b.c:d;e", ",;") == V({"a", "b.c:d", "e"})); + BOOST_TEST(SplitString("a,b.c:d;e", ",;:.") == V({"a", "b", "c", "d", "e"})); + BOOST_TEST(SplitString("a,b.c:d;e", "") == V({"a,b.c:d;e"})); + BOOST_TEST(SplitString("aaa", "bcdefg") == V({"aaa"})); + BOOST_TEST(SplitString("x\0a,b"s, "\0"s) == V({"x", "a,b"})); + BOOST_TEST(SplitString("x\0a,b"s, '\0') == V({"x", "a,b"})); + BOOST_TEST(SplitString("x\0a,b"s, "\0,"s) == V({"x", "a", "b"})); + BOOST_TEST(SplitString("abcdefg", "bcd") == V({"a", "", "", "efg"})); + } } BOOST_AUTO_TEST_CASE(test_LogEscapeMessage) diff --git a/src/util/spanparsing.h b/src/util/spanparsing.h index ebec8714a7..51795271de 100644 --- a/src/util/spanparsing.h +++ b/src/util/spanparsing.h @@ -8,6 +8,7 @@ #include #include +#include #include namespace spanparsing { @@ -36,6 +37,30 @@ bool Func(const std::string& str, Span& sp); */ Span Expr(Span& sp); +/** Split a string on any char found in separators, returning a vector. + * + * If sep does not occur in sp, a singleton with the entirety of sp is returned. + * + * Note that this function does not care about braces, so splitting + * "foo(bar(1),2),3) on ',' will return {"foo(bar(1)", "2)", "3)"}. + */ +template > +std::vector Split(const Span& sp, std::string_view separators) +{ + std::vector ret; + auto it = sp.begin(); + auto start = it; + while (it != sp.end()) { + if (separators.find(*it) != std::string::npos) { + ret.emplace_back(start, it); + start = it + 1; + } + ++it; + } + ret.emplace_back(start, it); + return ret; +} + /** Split a string on every instance of sep, returning a vector. * * If sep does not occur in sp, a singleton with the entirety of sp is returned. @@ -46,18 +71,7 @@ Span Expr(Span& sp); template > std::vector Split(const Span& sp, char sep) { - std::vector ret; - auto it = sp.begin(); - auto start = it; - while (it != sp.end()) { - if (*it == sep) { - ret.emplace_back(start, it); - start = it + 1; - } - ++it; - } - ret.emplace_back(start, it); - return ret; + return Split(sp, std::string_view{&sep, 1}); } } // namespace spanparsing diff --git a/src/util/string.h b/src/util/string.h index 36b9787db4..2e91347b27 100644 --- a/src/util/string.h +++ b/src/util/string.h @@ -14,6 +14,7 @@ #include #include #include +#include #include [[nodiscard]] inline std::vector SplitString(std::string_view str, char sep) @@ -21,6 +22,11 @@ return spanparsing::Split(str, sep); } +[[nodiscard]] inline std::vector SplitString(std::string_view str, std::string_view separators) +{ + return spanparsing::Split(str, separators); +} + [[nodiscard]] inline std::string_view TrimStringView(std::string_view str, std::string_view pattern = " \f\n\r\t\v") { std::string::size_type front = str.find_first_not_of(pattern);