summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormartinfalisse <martinmotteditfalisse@gmail.com>2023-01-16 17:12:53 +0100
committerAndreas Kling <kling@serenityos.org>2023-01-21 14:35:00 +0100
commitaec2dadfddf106fa06c10814ae6f2d0d5a56ea0c (patch)
tree5ab3c123b085d5532b186d9c8c1ab59b9943d48b
parent4f5353cbb8f01e17d0cc09a0244d9dc2ef4c37f2 (diff)
downloadserenity-aec2dadfddf106fa06c10814ae6f2d0d5a56ea0c.zip
AK: Add `split()` for `String`
-rw-r--r--AK/String.cpp30
-rw-r--r--AK/String.h4
-rw-r--r--Tests/AK/TestString.cpp20
3 files changed, 54 insertions, 0 deletions
diff --git a/AK/String.cpp b/AK/String.cpp
index f480f73d7d..1924aef113 100644
--- a/AK/String.cpp
+++ b/AK/String.cpp
@@ -251,6 +251,36 @@ ErrorOr<String> String::vformatted(StringView fmtstr, TypeErasedFormatParams& pa
return builder.to_string();
}
+ErrorOr<Vector<String>> String::split(u32 separator, SplitBehavior split_behavior) const
+{
+ return split_limit(separator, 0, split_behavior);
+}
+
+ErrorOr<Vector<String>> String::split_limit(u32 separator, size_t limit, SplitBehavior split_behavior) const
+{
+ Vector<String> result;
+
+ if (is_empty())
+ return result;
+
+ bool keep_empty = has_flag(split_behavior, SplitBehavior::KeepEmpty);
+
+ size_t substring_start = 0;
+ for (auto it = code_points().begin(); it != code_points().end() && (result.size() + 1) != limit; ++it) {
+ u32 code_point = *it;
+ if (code_point == separator) {
+ size_t substring_length = code_points().iterator_offset(it) - substring_start;
+ if (substring_length != 0 || keep_empty)
+ TRY(result.try_append(TRY(substring_from_byte_offset_with_shared_superstring(substring_start, substring_length))));
+ substring_start = code_points().iterator_offset(it) + it.underlying_code_point_length_in_bytes();
+ }
+ }
+ size_t tail_length = code_points().byte_length() - substring_start;
+ if (tail_length != 0 || keep_empty)
+ TRY(result.try_append(TRY(substring_from_byte_offset_with_shared_superstring(substring_start, tail_length))));
+ return result;
+}
+
bool String::operator==(String const& other) const
{
if (is_short_string())
diff --git a/AK/String.h b/AK/String.h
index 75f643bfd6..c5a22111a0 100644
--- a/AK/String.h
+++ b/AK/String.h
@@ -16,6 +16,7 @@
#include <AK/StringView.h>
#include <AK/Traits.h>
#include <AK/Types.h>
+#include <AK/Vector.h>
namespace AK {
@@ -101,6 +102,9 @@ public:
ErrorOr<String> replace(StringView needle, StringView replacement, ReplaceMode replace_mode) const;
ErrorOr<String> reverse() const;
+ [[nodiscard]] ErrorOr<Vector<String>> split_limit(u32 separator, size_t limit, SplitBehavior = SplitBehavior::Nothing) const;
+ [[nodiscard]] ErrorOr<Vector<String>> split(u32 separator, SplitBehavior = SplitBehavior::Nothing) const;
+
[[nodiscard]] bool operator==(String const&) const;
[[nodiscard]] bool operator!=(String const& other) const { return !(*this == other); }
diff --git a/Tests/AK/TestString.cpp b/Tests/AK/TestString.cpp
index c089cfcf72..c5785c7dab 100644
--- a/Tests/AK/TestString.cpp
+++ b/Tests/AK/TestString.cpp
@@ -272,3 +272,23 @@ TEST_CASE(is_one_of)
EXPECT(bar.is_one_of("bar"sv, "foo"sv));
EXPECT(bar.is_one_of("bar"sv));
}
+
+TEST_CASE(split)
+{
+ {
+ auto test = MUST(String::from_utf8("foo bar baz"sv));
+ auto parts = MUST(test.split(' '));
+ EXPECT_EQ(parts.size(), 3u);
+ EXPECT_EQ(parts[0], "foo");
+ EXPECT_EQ(parts[1], "bar");
+ EXPECT_EQ(parts[2], "baz");
+ }
+ {
+ auto test = MUST(String::from_utf8("ωΣ2ωΣω"sv));
+ auto parts = MUST(test.split(0x03A3u));
+ EXPECT_EQ(parts.size(), 3u);
+ EXPECT_EQ(parts[0], "ω"sv);
+ EXPECT_EQ(parts[1], "2ω"sv);
+ EXPECT_EQ(parts[2], "ω"sv);
+ }
+}