diff options
Diffstat (limited to 'core/string')
-rw-r--r-- | core/string/ustring.cpp | 436 | ||||
-rw-r--r-- | core/string/ustring.h | 17 |
2 files changed, 444 insertions, 9 deletions
diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp index 2b62b72a51..3d37e17ef8 100644 --- a/core/string/ustring.cpp +++ b/core/string/ustring.cpp @@ -1184,6 +1184,26 @@ int String::get_slice_count(const String &p_splitter) const { return slices; } +int String::get_slice_count(const char *p_splitter) const { + if (is_empty()) { + return 0; + } + if (p_splitter == nullptr || *p_splitter == '\0') { + return 0; + } + + int pos = 0; + int slices = 1; + int splitter_length = strlen(p_splitter); + + while ((pos = find(p_splitter, pos)) >= 0) { + slices++; + pos += splitter_length; + } + + return slices; +} + String String::get_slice(const String &p_splitter, int p_slice) const { if (is_empty() || p_splitter.is_empty()) { return ""; @@ -1224,6 +1244,47 @@ String String::get_slice(const String &p_splitter, int p_slice) const { return ""; //no find! } +String String::get_slice(const char *p_splitter, int p_slice) const { + if (is_empty() || p_splitter == nullptr || *p_splitter == '\0') { + return ""; + } + + int pos = 0; + int prev_pos = 0; + //int slices=1; + if (p_slice < 0) { + return ""; + } + if (find(p_splitter) == -1) { + return *this; + } + + int i = 0; + int splitter_length = strlen(p_splitter); + while (true) { + pos = find(p_splitter, pos); + if (pos == -1) { + pos = length(); //reached end + } + + int from = prev_pos; + //int to=pos; + + if (p_slice == i) { + return substr(from, pos - from); + } + + if (pos == length()) { //reached end and no find + break; + } + pos += splitter_length; + prev_pos = pos; + i++; + } + + return ""; //no find! +} + String String::get_slicec(char32_t p_splitter, int p_slice) const { if (is_empty()) { return String(); @@ -1338,6 +1399,54 @@ Vector<String> String::split(const String &p_splitter, bool p_allow_empty, int p return ret; } +Vector<String> String::split(const char *p_splitter, bool p_allow_empty, int p_maxsplit) const { + Vector<String> ret; + + if (is_empty()) { + if (p_allow_empty) { + ret.push_back(""); + } + return ret; + } + + int from = 0; + int len = length(); + + while (true) { + int end; + if (p_splitter == nullptr || *p_splitter == '\0') { + end = from + 1; + } else { + end = find(p_splitter, from); + if (end < 0) { + end = len; + } + } + if (p_allow_empty || (end > from)) { + if (p_maxsplit <= 0) { + ret.push_back(substr(from, end - from)); + } else { + // Put rest of the string and leave cycle. + if (p_maxsplit == ret.size()) { + ret.push_back(substr(from, len)); + break; + } + + // Otherwise, push items until positive limit is reached. + ret.push_back(substr(from, end - from)); + } + } + + if (end == len) { + break; + } + + from = end + strlen(p_splitter); + } + + return ret; +} + Vector<String> String::rsplit(const String &p_splitter, bool p_allow_empty, int p_maxsplit) const { Vector<String> ret; const int len = length(); @@ -1380,6 +1489,49 @@ Vector<String> String::rsplit(const String &p_splitter, bool p_allow_empty, int return ret; } +Vector<String> String::rsplit(const char *p_splitter, bool p_allow_empty, int p_maxsplit) const { + Vector<String> ret; + const int len = length(); + const int splitter_length = strlen(p_splitter); + int remaining_len = len; + + while (true) { + if (remaining_len < splitter_length || (p_maxsplit > 0 && p_maxsplit == ret.size())) { + // no room for another splitter or hit max splits, push what's left and we're done + if (p_allow_empty || remaining_len > 0) { + ret.push_back(substr(0, remaining_len)); + } + break; + } + + int left_edge; + if (p_splitter == nullptr || *p_splitter == '\0') { + left_edge = remaining_len - 1; + if (left_edge == 0) { + left_edge--; // Skip to the < 0 condition. + } + } else { + left_edge = rfind(p_splitter, remaining_len - splitter_length); + } + + if (left_edge < 0) { + // no more splitters, we're done + ret.push_back(substr(0, remaining_len)); + break; + } + + int substr_start = left_edge + splitter_length; + if (p_allow_empty || substr_start < remaining_len) { + ret.push_back(substr(substr_start, remaining_len - substr_start)); + } + + remaining_len = left_edge; + } + + ret.reverse(); + return ret; +} + Vector<double> String::split_floats(const String &p_splitter, bool p_allow_empty) const { Vector<double> ret; int from = 0; @@ -3087,23 +3239,20 @@ int String::find(const String &p_str, int p_from) const { } int String::find(const char *p_str, int p_from) const { - if (p_from < 0) { + if (p_from < 0 || !p_str) { return -1; } + const int src_len = strlen(p_str); + const int len = length(); - if (len == 0) { + if (len == 0 || src_len == 0) { return -1; // won't find anything! } const char32_t *src = get_data(); - int src_len = 0; - while (p_str[src_len] != '\0') { - src_len++; - } - if (src_len == 1) { const char32_t needle = p_str[0]; @@ -3238,6 +3387,46 @@ int String::findn(const String &p_str, int p_from) const { return -1; } +int String::findn(const char *p_str, int p_from) const { + if (p_from < 0) { + return -1; + } + + int src_len = strlen(p_str); + + if (src_len == 0 || length() == 0) { + return -1; // won't find anything! + } + + const char32_t *srcd = get_data(); + + for (int i = p_from; i <= (length() - src_len); i++) { + bool found = true; + for (int j = 0; j < src_len; j++) { + int read_pos = i + j; + + if (read_pos >= length()) { + ERR_PRINT("read_pos>=length()"); + return -1; + } + + char32_t src = _find_lower(srcd[read_pos]); + char32_t dst = _find_lower(p_str[j]); + + if (src != dst) { + found = false; + break; + } + } + + if (found) { + return i; + } + } + + return -1; +} + int String::rfind(const String &p_str, int p_from) const { // establish a limit int limit = length() - p_str.length(); @@ -3285,6 +3474,57 @@ int String::rfind(const String &p_str, int p_from) const { return -1; } +int String::rfind(const char *p_str, int p_from) const { + const int source_length = length(); + int substring_length = strlen(p_str); + + if (source_length == 0 || substring_length == 0) { + return -1; // won't find anything! + } + + // establish a limit + int limit = length() - substring_length; + if (limit < 0) { + return -1; + } + + // establish a starting point + int starting_point; + if (p_from < 0) { + starting_point = limit; + } else if (p_from > limit) { + starting_point = limit; + } else { + starting_point = p_from; + } + + const char32_t *source = get_data(); + + for (int i = starting_point; i >= 0; i--) { + bool found = true; + for (int j = 0; j < substring_length; j++) { + int read_pos = i + j; + + if (read_pos >= source_length) { + ERR_PRINT("read_pos>=source_length"); + return -1; + } + + const char32_t key_needle = p_str[j]; + if (source[read_pos] != key_needle) { + found = false; + break; + } + } + + if (found) { + return i; + } + } + + return -1; +} + int String::rfindn(const String &p_str, int p_from) const { // establish a limit int limit = length() - p_str.length(); @@ -3335,6 +3575,60 @@ int String::rfindn(const String &p_str, int p_from) const { return -1; } +int String::rfindn(const char *p_str, int p_from) const { + const int source_length = length(); + int substring_length = strlen(p_str); + + if (source_length == 0 || substring_length == 0) { + return -1; // won't find anything! + } + + // establish a limit + int limit = length() - substring_length; + if (limit < 0) { + return -1; + } + + // establish a starting point + int starting_point; + if (p_from < 0) { + starting_point = limit; + } else if (p_from > limit) { + starting_point = limit; + } else { + starting_point = p_from; + } + + const char32_t *source = get_data(); + + for (int i = starting_point; i >= 0; i--) { + bool found = true; + for (int j = 0; j < substring_length; j++) { + int read_pos = i + j; + + if (read_pos >= source_length) { + ERR_PRINT("read_pos>=source_length"); + return -1; + } + + const char32_t key_needle = p_str[j]; + int srcc = _find_lower(source[read_pos]); + int keyc = _find_lower(key_needle); + + if (srcc != keyc) { + found = false; + break; + } + } + + if (found) { + return i; + } + } + + return -1; +} + bool String::ends_with(const String &p_string) const { int l = p_string.length(); if (l > length()) { @@ -3357,6 +3651,31 @@ bool String::ends_with(const String &p_string) const { return true; } +bool String::ends_with(const char *p_string) const { + if (!p_string) { + return false; + } + + int l = strlen(p_string); + if (l > length()) { + return false; + } + + if (l == 0) { + return true; + } + + const char32_t *s = &operator[](length() - l); + + for (int i = 0; i < l; i++) { + if (static_cast<char32_t>(p_string[i]) != s[i]) { + return false; + } + } + + return true; +} + bool String::begins_with(const String &p_string) const { int l = p_string.length(); if (l > length()) { @@ -3380,11 +3699,11 @@ bool String::begins_with(const String &p_string) const { } bool String::begins_with(const char *p_string) const { - int l = length(); if (!p_string) { return false; } + int l = length(); if (l == 0) { return *p_string == 0; } @@ -3456,14 +3775,61 @@ int String::_count(const String &p_string, int p_from, int p_to, bool p_case_ins return c; } +int String::_count(const char *p_string, int p_from, int p_to, bool p_case_insensitive) const { + int substring_length = strlen(p_string); + if (substring_length == 0) { + return 0; + } + const int source_length = length(); + + if (source_length < substring_length) { + return 0; + } + String str; + int search_limit = p_to; + if (p_from >= 0 && p_to >= 0) { + if (p_to == 0) { + search_limit = source_length; + } else if (p_from >= p_to) { + return 0; + } + if (p_from == 0 && search_limit == source_length) { + str = String(); + str.copy_from_unchecked(&get_data()[0], source_length); + } else { + str = substr(p_from, search_limit - p_from); + } + } else { + return 0; + } + int c = 0; + int idx = -1; + do { + idx = p_case_insensitive ? str.findn(p_string) : str.find(p_string); + if (idx != -1) { + str = str.substr(idx + substring_length, str.length() - substring_length); + ++c; + } + } while (idx != -1); + return c; +} + int String::count(const String &p_string, int p_from, int p_to) const { return _count(p_string, p_from, p_to, false); } +int String::count(const char *p_string, int p_from, int p_to) const { + return _count(p_string, p_from, p_to, false); +} + int String::countn(const String &p_string, int p_from, int p_to) const { return _count(p_string, p_from, p_to, true); } +int String::countn(const char *p_string, int p_from, int p_to) const { + return _count(p_string, p_from, p_to, true); +} + bool String::_base_is_subsequence_of(const String &p_string, bool case_insensitive) const { int len = length(); if (len == 0) { @@ -3598,7 +3964,7 @@ String String::format(const Variant &values, const String &placeholder) const { Variant v_val = values_arr[i]; String val = v_val; - if (placeholder.find("_") > -1) { + if (placeholder.contains("_")) { new_string = new_string.replace(placeholder.replace("_", i_as_str), val); } else { new_string = new_string.replace_first(placeholder, val); @@ -3673,6 +4039,16 @@ String String::replace_first(const String &p_key, const String &p_with) const { return *this; } +String String::replace_first(const char *p_key, const char *p_with) const { + int pos = find(p_key); + if (pos >= 0) { + int substring_length = strlen(p_key); + return substr(0, pos) + p_with + substr(pos + substring_length, length()); + } + + return *this; +} + String String::replacen(const String &p_key, const String &p_with) const { String new_string; int search_from = 0; @@ -3692,6 +4068,31 @@ String String::replacen(const String &p_key, const String &p_with) const { return new_string; } +String String::replacen(const char *p_key, const char *p_with) const { + String new_string; + int search_from = 0; + int result = 0; + int substring_length = strlen(p_key); + + if (substring_length == 0) { + return *this; // there's nothing to match or substitute + } + + while ((result = findn(p_key, search_from)) >= 0) { + new_string += substr(search_from, result - search_from); + new_string += p_with; + search_from = result + substring_length; + } + + if (search_from == 0) { + return *this; + } + + new_string += substr(search_from, length() - search_from); + + return new_string; +} + String String::repeat(int p_count) const { ERR_FAIL_COND_V_MSG(p_count < 0, "", "Parameter count should be a positive number."); @@ -4420,6 +4821,15 @@ String String::trim_prefix(const String &p_prefix) const { return s; } +String String::trim_prefix(const char *p_prefix) const { + String s = *this; + if (s.begins_with(p_prefix)) { + int prefix_length = strlen(p_prefix); + return s.substr(prefix_length, s.length() - prefix_length); + } + return s; +} + String String::trim_suffix(const String &p_suffix) const { String s = *this; if (s.ends_with(p_suffix)) { @@ -4428,6 +4838,14 @@ String String::trim_suffix(const String &p_suffix) const { return s; } +String String::trim_suffix(const char *p_suffix) const { + String s = *this; + if (s.ends_with(p_suffix)) { + return s.substr(0, s.length() - strlen(p_suffix)); + } + return s; +} + bool String::is_valid_int() const { int len = length(); diff --git a/core/string/ustring.h b/core/string/ustring.h index 693df6dcba..9df2d56e80 100644 --- a/core/string/ustring.h +++ b/core/string/ustring.h @@ -198,6 +198,7 @@ class String { bool _base_is_subsequence_of(const String &p_string, bool case_insensitive) const; int _count(const String &p_string, int p_from, int p_to, bool p_case_insensitive) const; + int _count(const char *p_string, int p_from, int p_to, bool p_case_insensitive) const; String _camelcase_to_underscore() const; public: @@ -288,14 +289,18 @@ public: int find(const char *p_str, int p_from = 0) const; ///< return <0 if failed int find_char(const char32_t &p_char, int p_from = 0) const; ///< return <0 if failed int findn(const String &p_str, int p_from = 0) const; ///< return <0 if failed, case insensitive + int findn(const char *p_str, int p_from = 0) const; ///< return <0 if failed int rfind(const String &p_str, int p_from = -1) const; ///< return <0 if failed + int rfind(const char *p_str, int p_from = -1) const; ///< return <0 if failed int rfindn(const String &p_str, int p_from = -1) const; ///< return <0 if failed, case insensitive + int rfindn(const char *p_str, int p_from = -1) const; ///< return <0 if failed int findmk(const Vector<String> &p_keys, int p_from = 0, int *r_key = nullptr) const; ///< return <0 if failed bool match(const String &p_wildcard) const; bool matchn(const String &p_wildcard) const; bool begins_with(const String &p_string) const; bool begins_with(const char *p_string) const; bool ends_with(const String &p_string) const; + bool ends_with(const char *p_string) const; bool is_enclosed_in(const String &p_string) const; bool is_subsequence_of(const String &p_string) const; bool is_subsequence_ofn(const String &p_string) const; @@ -304,9 +309,11 @@ public: float similarity(const String &p_string) const; String format(const Variant &values, const String &placeholder = "{_}") const; String replace_first(const String &p_key, const String &p_with) const; + String replace_first(const char *p_key, const char *p_with) const; String replace(const String &p_key, const String &p_with) const; String replace(const char *p_key, const char *p_with) const; String replacen(const String &p_key, const String &p_with) const; + String replacen(const char *p_key, const char *p_with) const; String repeat(int p_count) const; String reverse() const; String insert(int p_at_pos, const String &p_string) const; @@ -314,7 +321,9 @@ public: String pad_decimals(int p_digits) const; String pad_zeros(int p_digits) const; String trim_prefix(const String &p_prefix) const; + String trim_prefix(const char *p_prefix) const; String trim_suffix(const String &p_suffix) const; + String trim_suffix(const char *p_suffix) const; String lpad(int min_length, const String &character = " ") const; String rpad(int min_length, const String &character = " ") const; String sprintf(const Array &values, bool *error) const; @@ -353,11 +362,15 @@ public: String get_with_code_lines() const; int get_slice_count(const String &p_splitter) const; + int get_slice_count(const char *p_splitter) const; String get_slice(const String &p_splitter, int p_slice) const; + String get_slice(const char *p_splitter, int p_slice) const; String get_slicec(char32_t p_splitter, int p_slice) const; Vector<String> split(const String &p_splitter = "", bool p_allow_empty = true, int p_maxsplit = 0) const; + Vector<String> split(const char *p_splitter = "", bool p_allow_empty = true, int p_maxsplit = 0) const; Vector<String> rsplit(const String &p_splitter = "", bool p_allow_empty = true, int p_maxsplit = 0) const; + Vector<String> rsplit(const char *p_splitter = "", bool p_allow_empty = true, int p_maxsplit = 0) const; Vector<String> split_spaces() const; Vector<double> split_floats(const String &p_splitter, bool p_allow_empty = true) const; Vector<float> split_floats_mk(const Vector<String> &p_splitters, bool p_allow_empty = true) const; @@ -372,7 +385,9 @@ public: String to_lower() const; int count(const String &p_string, int p_from = 0, int p_to = 0) const; + int count(const char *p_string, int p_from = 0, int p_to = 0) const; int countn(const String &p_string, int p_from = 0, int p_to = 0) const; + int countn(const char *p_string, int p_from = 0, int p_to = 0) const; String left(int p_len) const; String right(int p_len) const; @@ -414,6 +429,8 @@ public: _FORCE_INLINE_ bool is_empty() const { return length() == 0; } _FORCE_INLINE_ bool contains(const char *p_str) const { return find(p_str) != -1; } _FORCE_INLINE_ bool contains(const String &p_str) const { return find(p_str) != -1; } + _FORCE_INLINE_ bool containsn(const char *p_str) const { return findn(p_str) != -1; } + _FORCE_INLINE_ bool containsn(const String &p_str) const { return findn(p_str) != -1; } // path functions bool is_absolute_path() const; |