aboutsummaryrefslogtreecommitdiff
path: root/include/cru/base/StringUtil.h
diff options
context:
space:
mode:
Diffstat (limited to 'include/cru/base/StringUtil.h')
-rw-r--r--include/cru/base/StringUtil.h47
1 files changed, 47 insertions, 0 deletions
diff --git a/include/cru/base/StringUtil.h b/include/cru/base/StringUtil.h
index 8f085283..ef6359ff 100644
--- a/include/cru/base/StringUtil.h
+++ b/include/cru/base/StringUtil.h
@@ -1,10 +1,57 @@
#pragma once
#include "Base.h"
+#include "Bitmask.h"
#include <functional>
+#include <stdexcept>
#include <type_traits>
+#include <vector>
namespace cru {
+namespace details {
+struct SplitOptionsTag {};
+} // namespace details
+using SplitOption = Bitmask<details::SplitOptionsTag>;
+struct SplitOptions {
+ static constexpr SplitOption RemoveEmpty = SplitOption::FromOffset(1);
+};
+
+template <typename TString>
+std::vector<TString> Split(const TString& str, const TString& sep,
+ SplitOption options = {}) {
+ using size_type = typename TString::size_type;
+
+ if (sep.empty()) throw std::invalid_argument("Sep can't be empty.");
+ if (str.empty()) return {};
+
+ size_type current_pos = 0;
+ std::vector<TString> result;
+
+ while (current_pos != TString::npos) {
+ if (current_pos == str.size()) {
+ if (!options.Has(SplitOptions::RemoveEmpty)) {
+ result.push_back({});
+ }
+ break;
+ }
+
+ auto next_pos = str.find(sep, current_pos);
+ auto sub = str.substr(current_pos, next_pos - current_pos);
+ if (!(options.Has(SplitOptions::RemoveEmpty) && sub.empty())) {
+ result.push_back(sub);
+ }
+ current_pos =
+ next_pos == TString::npos ? TString::npos : next_pos + sep.size();
+ }
+
+ return result;
+}
+
+inline std::vector<std::string> Split(const char* str, const std::string& sep,
+ SplitOption options = {}) {
+ return Split<std::string>(std::string(str), sep, options);
+}
+
using CodePoint = std::int32_t;
constexpr CodePoint k_invalid_code_point = -1;