diff --git a/Source/Core/Common/StringUtil.h b/Source/Core/Common/StringUtil.h index ea82581355..6e2c385df9 100644 --- a/Source/Core/Common/StringUtil.h +++ b/Source/Core/Common/StringUtil.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -182,6 +183,32 @@ std::from_chars_result FromChars(std::string_view sv, std::floating_point auto& std::vector SplitString(const std::string& str, char delim); +// Returns `nullopt` if subject does not contain exactly (Count - 1) delim. +template +requires(Count > 1) +std::optional> SplitStringIntoArray(std::string_view subject, + char delim) +{ + std::optional> result; + result.emplace(); + + std::size_t index = 0; + for (auto&& item : subject | std::views::split(delim)) + { + // Too many delim. + if (index == Count) + return std::nullopt; + + (*result)[index++] = std::string_view{item}; + } + + // Too few delim. + if (index != Count) + return std::nullopt; + + return result; +} + // "C:/Windows/winhelp.exe" to "C:/Windows/", "winhelp", ".exe" // This requires forward slashes to be used for the path separators, even on Windows. bool SplitPath(std::string_view full_path, std::string* path, std::string* filename, diff --git a/Source/UnitTests/Common/StringUtilTest.cpp b/Source/UnitTests/Common/StringUtilTest.cpp index 59ba99a436..d7c783497a 100644 --- a/Source/UnitTests/Common/StringUtilTest.cpp +++ b/Source/UnitTests/Common/StringUtilTest.cpp @@ -259,6 +259,29 @@ TEST(StringUtil, CaseInsensitiveContains_OverlappingMatches) EXPECT_TRUE(Common::CaseInsensitiveContains("ababababa", "bABa")); } +TEST(StringUtil, SplitStringIntoArray) +{ + constexpr auto subject0 = ""; + EXPECT_FALSE(SplitStringIntoArray<2>(subject0, '.').has_value()); + + constexpr auto subject1 = "hello"; + EXPECT_FALSE(SplitStringIntoArray<2>(subject1, '.').has_value()); + + constexpr auto subject2 = "hello.world"; + const auto split2 = SplitStringIntoArray<2>(subject2, '.'); + EXPECT_TRUE(split2.has_value() && split2->at(1) == "world"); + EXPECT_FALSE(SplitStringIntoArray<3>(subject2, '.').has_value()); + + constexpr auto subject3 = "hello.universe."; + EXPECT_FALSE(SplitStringIntoArray<2>(subject3, '.').has_value()); + const auto split3 = SplitStringIntoArray<3>(subject3, '.'); + EXPECT_TRUE(split3.has_value() && split3->at(2) == ""); + EXPECT_FALSE(SplitStringIntoArray<4>(subject3, '.').has_value()); + + constexpr auto subject4 = "="; + EXPECT_TRUE(SplitStringIntoArray<2>(subject4, '=').has_value()); +} + TEST(StringUtil, CharacterEncodingConversion) { // wstring