AK: Add SplitBehavior::KeepTrailingSeparator with tests

This commit is contained in:
demostanis 2022-10-22 16:31:59 +02:00 committed by Linus Groh
parent 3e8b5ac920
commit 7c33f8f7df
Notes: sideshowbarker 2024-07-17 05:08:08 +09:00
5 changed files with 26 additions and 6 deletions

View file

@ -110,12 +110,13 @@ Vector<String> String::split_limit(char separator, size_t limit, SplitBehavior s
Vector<String> v; Vector<String> v;
size_t substart = 0; size_t substart = 0;
bool keep_empty = has_flag(split_behavior, SplitBehavior::KeepEmpty); bool keep_empty = has_flag(split_behavior, SplitBehavior::KeepEmpty);
bool keep_separator = has_flag(split_behavior, SplitBehavior::KeepTrailingSeparator);
for (size_t i = 0; i < length() && (v.size() + 1) != limit; ++i) { for (size_t i = 0; i < length() && (v.size() + 1) != limit; ++i) {
char ch = characters()[i]; char ch = characters()[i];
if (ch == separator) { if (ch == separator) {
size_t sublen = i - substart; size_t sublen = i - substart;
if (sublen != 0 || keep_empty) if (sublen != 0 || keep_empty)
v.append(substring(substart, sublen)); v.append(substring(substart, keep_separator ? sublen + 1 : sublen));
substart = i + 1; substart = i + 1;
} }
} }
@ -133,12 +134,13 @@ Vector<StringView> String::split_view(Function<bool(char)> separator, SplitBehav
Vector<StringView> v; Vector<StringView> v;
size_t substart = 0; size_t substart = 0;
bool keep_empty = has_flag(split_behavior, SplitBehavior::KeepEmpty); bool keep_empty = has_flag(split_behavior, SplitBehavior::KeepEmpty);
bool keep_separator = has_flag(split_behavior, SplitBehavior::KeepTrailingSeparator);
for (size_t i = 0; i < length(); ++i) { for (size_t i = 0; i < length(); ++i) {
char ch = characters()[i]; char ch = characters()[i];
if (separator(ch)) { if (separator(ch)) {
size_t sublen = i - substart; size_t sublen = i - substart;
if (sublen != 0 || keep_empty) if (sublen != 0 || keep_empty)
v.append(substring_view(substart, sublen)); v.append(substring_view(substart, keep_separator ? sublen + 1 : sublen));
substart = i + 1; substart = i + 1;
} }
} }

View file

@ -40,10 +40,17 @@ enum class TrimWhitespace {
}; };
enum class SplitBehavior : unsigned { enum class SplitBehavior : unsigned {
// Neither keep empty substrings nor keep the trailing separator.
// This is the default behavior if unspecified.
Nothing = 0, Nothing = 0,
// If two separators follow each other without any characters // If two separators follow each other without any characters
// in between, keep a "" in the resulting vector. // in between, keep a "" in the resulting vector. (or only the
// separator if KeepTrailingSeparator is used)
KeepEmpty = 1, KeepEmpty = 1,
// Do not strip off the separator at the end of the string.
KeepTrailingSeparator = 2,
}; };
AK_ENUM_BITWISE_OPERATORS(SplitBehavior); AK_ENUM_BITWISE_OPERATORS(SplitBehavior);

View file

@ -272,12 +272,13 @@ Vector<StringView> StringView::split_view_if(Function<bool(char)> const& predica
Vector<StringView> v; Vector<StringView> v;
size_t substart = 0; size_t substart = 0;
bool keep_empty = has_flag(split_behavior, SplitBehavior::KeepEmpty); bool keep_empty = has_flag(split_behavior, SplitBehavior::KeepEmpty);
bool keep_separator = has_flag(split_behavior, SplitBehavior::KeepTrailingSeparator);
for (size_t i = 0; i < length(); ++i) { for (size_t i = 0; i < length(); ++i) {
char ch = characters_without_null_termination()[i]; char ch = characters_without_null_termination()[i];
if (predicate(ch)) { if (predicate(ch)) {
size_t sublen = i - substart; size_t sublen = i - substart;
if (sublen != 0 || keep_empty) if (sublen != 0 || keep_empty)
v.append(substring_view(substart, sublen)); v.append(substring_view(substart, keep_separator ? sublen + 1 : sublen));
substart = i + 1; substart = i + 1;
} }
} }

View file

@ -170,11 +170,16 @@ public:
auto maybe_separator_index = find(separator); auto maybe_separator_index = find(separator);
bool keep_empty = has_flag(split_behavior, SplitBehavior::KeepEmpty); bool keep_empty = has_flag(split_behavior, SplitBehavior::KeepEmpty);
bool keep_separator = has_flag(split_behavior, SplitBehavior::KeepTrailingSeparator);
while (maybe_separator_index.has_value()) { while (maybe_separator_index.has_value()) {
auto separator_index = maybe_separator_index.value(); auto separator_index = maybe_separator_index.value();
auto part_with_separator = view.substring_view(0, separator_index + separator.length()); auto part_with_separator = view.substring_view(0, separator_index + separator.length());
if (keep_empty || separator_index > 0) if (keep_empty || separator_index > 0) {
callback(part_with_separator.substring_view(0, separator_index)); if (keep_separator)
callback(part_with_separator);
else
callback(part_with_separator.substring_view(0, separator_index));
}
view = view.substring_view_starting_after_substring(part_with_separator); view = view.substring_view_starting_after_substring(part_with_separator);
maybe_separator_index = view.find(separator); maybe_separator_index = view.find(separator);
} }

View file

@ -163,6 +163,11 @@ TEST_CASE(split_view)
EXPECT_EQ(test_string_view.split_view_if(predicate, SplitBehavior::KeepEmpty), Vector({ "a"sv, ""sv, "b"sv, "c"sv, "d"sv })); EXPECT_EQ(test_string_view.split_view_if(predicate, SplitBehavior::KeepEmpty), Vector({ "a"sv, ""sv, "b"sv, "c"sv, "d"sv }));
EXPECT_EQ(test_string_view.split_view_if(predicate), Vector({ "a"sv, "b"sv, "c"sv, "d"sv })); EXPECT_EQ(test_string_view.split_view_if(predicate), Vector({ "a"sv, "b"sv, "c"sv, "d"sv }));
EXPECT_EQ(test_string_view.split_view_if(predicate, SplitBehavior::KeepEmpty), Vector({ "a"sv, ""sv, "b"sv, "c"sv, "d"sv })); EXPECT_EQ(test_string_view.split_view_if(predicate, SplitBehavior::KeepEmpty), Vector({ "a"sv, ""sv, "b"sv, "c"sv, "d"sv }));
test_string_view = "a,,,b"sv;
EXPECT_EQ(test_string_view.split_view(","sv, SplitBehavior::KeepEmpty), Vector({ "a"sv, ""sv, ""sv, "b"sv }));
EXPECT_EQ(test_string_view.split_view(","sv, SplitBehavior::KeepTrailingSeparator), Vector({ "a,"sv, "b"sv }));
EXPECT_EQ(test_string_view.split_view(","sv, SplitBehavior::KeepTrailingSeparator | SplitBehavior::KeepEmpty), Vector({ "a,"sv, ","sv, ","sv, "b"sv }));
} }
TEST_CASE(constexpr_stuff) TEST_CASE(constexpr_stuff)