summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJordan Woyak <jordan.woyak@gmail.com>2026-02-06 20:14:26 -0600
committerJordan Woyak <jordan.woyak@gmail.com>2026-02-15 20:14:15 -0600
commit97ad5ad1a1c71de3931b8b31da24fb09cfc2aee0 (patch)
treee929eac3ed9651609a4e7d535466aa0547d6a7bd
parent84b136c99473b0e1d3b9e1f200ad95e0aec2dac1 (diff)
StringUtil: Add SplitStringIntoArray function template.
-rw-r--r--Source/Core/Common/StringUtil.h27
-rw-r--r--Source/UnitTests/Common/StringUtilTest.cpp23
2 files changed, 50 insertions, 0 deletions
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 <iomanip>
#include <limits>
#include <locale>
+#include <ranges>
#include <span>
#include <sstream>
#include <string>
@@ -182,6 +183,32 @@ std::from_chars_result FromChars(std::string_view sv, std::floating_point auto&
std::vector<std::string> SplitString(const std::string& str, char delim);
+// Returns `nullopt` if subject does not contain exactly (Count - 1) delim.
+template <std::size_t Count>
+requires(Count > 1)
+std::optional<std::array<std::string_view, Count>> SplitStringIntoArray(std::string_view subject,
+ char delim)
+{
+ std::optional<std::array<std::string_view, Count>> 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