diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index 00b0c4de64b..4f4c946462c 100644 --- a/core/config/project_settings.cpp +++ b/core/config/project_settings.cpp @@ -1141,7 +1141,7 @@ Error ProjectSettings::save_custom(const String &p_path, const CustomMap &p_cust save_features += ","; } - String f = p_custom_features[i].strip_edges().replace("\"", ""); + String f = p_custom_features[i].strip_edges().remove_char('\"'); save_features += f; } diff --git a/core/extension/extension_api_dump.cpp b/core/extension/extension_api_dump.cpp index 56aec50641f..315a5d874e9 100644 --- a/core/extension/extension_api_dump.cpp +++ b/core/extension/extension_api_dump.cpp @@ -96,8 +96,7 @@ static String fix_doc_description(const String &p_bbcode) { // Based on what EditorHelp does. return p_bbcode.dedent() - .replace("\t", "") - .replace("\r", "") + .remove_chars("\t\r") .strip_edges(); } diff --git a/core/input/input.cpp b/core/input/input.cpp index 6c653c6dc30..6b9fec6f600 100644 --- a/core/input/input.cpp +++ b/core/input/input.cpp @@ -1594,8 +1594,8 @@ void Input::parse_mapping(const String &p_mapping) { continue; } - String output = entry[idx].get_slicec(':', 0).replace(" ", ""); - String input = entry[idx].get_slicec(':', 1).replace(" ", ""); + String output = entry[idx].get_slicec(':', 0).remove_char(' '); + String input = entry[idx].get_slicec(':', 1).remove_char(' '); if (output.length() < 1 || input.length() < 2) { continue; } diff --git a/core/io/dir_access.cpp b/core/io/dir_access.cpp index 4bdfc5a51cd..c90e376e262 100644 --- a/core/io/dir_access.cpp +++ b/core/io/dir_access.cpp @@ -336,7 +336,7 @@ Ref DirAccess::create_temp(const String &p_prefix, bool p_keep, Error uint32_t suffix_i = 0; String path; while (true) { - String datetime = Time::get_singleton()->get_datetime_string_from_system().replace("-", "").replace("T", "").replace(":", ""); + String datetime = Time::get_singleton()->get_datetime_string_from_system().remove_chars("-T:"); datetime += itos(Time::get_singleton()->get_ticks_usec()); String suffix = datetime + (suffix_i > 0 ? itos(suffix_i) : ""); path = (p_prefix.is_empty() ? "" : p_prefix + "-") + suffix; diff --git a/core/io/file_access.cpp b/core/io/file_access.cpp index 9d8479b2dc2..8aec97d9460 100644 --- a/core/io/file_access.cpp +++ b/core/io/file_access.cpp @@ -104,7 +104,7 @@ Ref FileAccess::create_temp(int p_mode_flags, const String &p_prefix uint32_t suffix_i = 0; String path; while (true) { - String datetime = Time::get_singleton()->get_datetime_string_from_system().replace("-", "").replace("T", "").replace(":", ""); + String datetime = Time::get_singleton()->get_datetime_string_from_system().remove_chars("-T:"); datetime += itos(Time::get_singleton()->get_ticks_usec()); String suffix = datetime + (suffix_i > 0 ? itos(suffix_i) : ""); path = TEMP_DIR.path_join((p_prefix.is_empty() ? "" : p_prefix + "-") + suffix + (extension.is_empty() ? "" : "." + extension)); diff --git a/core/math/color.cpp b/core/math/color.cpp index 5a8459abdfa..4cc1f73d99d 100644 --- a/core/math/color.cpp +++ b/core/math/color.cpp @@ -415,18 +415,14 @@ Color Color::named(const String &p_name, const Color &p_default) { int Color::find_named_color(const String &p_name) { String name = p_name; // Normalize name. - name = name.replace(" ", ""); - name = name.replace("-", ""); - name = name.replace("_", ""); - name = name.replace("'", ""); - name = name.replace(".", ""); + name = name.remove_chars(" -_'."); name = name.to_upper(); static HashMap named_colors_hashmap; if (unlikely(named_colors_hashmap.is_empty())) { const int named_color_count = get_named_color_count(); for (int i = 0; i < named_color_count; i++) { - named_colors_hashmap[String(named_colors[i].name).replace("_", "")] = i; + named_colors_hashmap[String(named_colors[i].name).remove_char('_')] = i; } } diff --git a/core/os/os.cpp b/core/os/os.cpp index 46e519304ef..bbeb5b1fb28 100644 --- a/core/os/os.cpp +++ b/core/os/os.cpp @@ -214,7 +214,7 @@ String OS::get_locale() const { // Non-virtual helper to extract the 2 or 3-letter language code from // `get_locale()` in a way that's consistent for all platforms. String OS::get_locale_language() const { - return get_locale().left(3).replace("_", ""); + return get_locale().left(3).remove_char('_'); } // Embedded PCK offset. diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp index 49c46747c0f..5d2e739a853 100644 --- a/core/string/ustring.cpp +++ b/core/string/ustring.cpp @@ -1007,7 +1007,7 @@ String String::to_camel_case() const { } String String::to_pascal_case() const { - return capitalize().replace(" ", ""); + return capitalize().remove_char(' '); } String String::to_snake_case() const { @@ -3102,6 +3102,130 @@ String String::erase(int p_pos, int p_chars) const { return left(p_pos) + substr(p_pos + p_chars); } +template +static bool _contains_char(char32_t p_c, const T *p_chars, int p_chars_len) { + for (int i = 0; i < p_chars_len; ++i) { + if (p_c == (char32_t)p_chars[i]) { + return true; + } + } + + return false; +} + +String String::remove_char(char32_t p_char) const { + if (p_char == 0) { + return *this; + } + + int len = length(); + if (len == 0) { + return *this; + } + + int index = 0; + const char32_t *old_ptr = ptr(); + for (; index < len; ++index) { + if (old_ptr[index] == p_char) { + break; + } + } + + // If no occurrence of `char` was found, return this. + if (index == len) { + return *this; + } + + // If we found at least one occurrence of `char`, create new string, allocating enough space for the current length minus one. + String new_string; + new_string.resize(len); + char32_t *new_ptr = new_string.ptrw(); + + // Copy part of input before `char`. + memcpy(new_ptr, old_ptr, index * sizeof(char32_t)); + + int new_size = index; + + // Copy rest, skipping `char`. + for (++index; index < len; ++index) { + const char32_t old_char = old_ptr[index]; + if (old_char != p_char) { + new_ptr[new_size] = old_char; + ++new_size; + } + } + + new_ptr[new_size] = _null; + + // Shrink new string to fit. + new_string.resize(new_size + 1); + + return new_string; +} + +template +static String _remove_chars_common(const String &p_this, const T *p_chars, int p_chars_len) { + // Delegate if p_chars has a single element. + if (p_chars_len == 1) { + return p_this.remove_char(*p_chars); + } else if (p_chars_len == 0) { + return p_this; + } + + int len = p_this.length(); + + if (len == 0) { + return p_this; + } + + int index = 0; + const char32_t *old_ptr = p_this.ptr(); + for (; index < len; ++index) { + if (_contains_char(old_ptr[index], p_chars, p_chars_len)) { + break; + } + } + + // If no occurrence of `chars` was found, return this. + if (index == len) { + return p_this; + } + + // If we found at least one occurrence of `chars`, create new string, allocating enough space for the current length minus one. + String new_string; + new_string.resize(len); + char32_t *new_ptr = new_string.ptrw(); + + // Copy part of input before `char`. + memcpy(new_ptr, old_ptr, index * sizeof(char32_t)); + + int new_size = index; + + // Copy rest, skipping `chars`. + for (++index; index < len; ++index) { + const char32_t old_char = old_ptr[index]; + if (!_contains_char(old_char, p_chars, p_chars_len)) { + new_ptr[new_size] = old_char; + ++new_size; + } + } + + new_ptr[new_size] = 0; + + // Shrink new string to fit. + new_string.resize(new_size + 1); + + return new_string; +} + +String String::remove_chars(const String &p_chars) const { + return _remove_chars_common(*this, p_chars.ptr(), p_chars.length()); +} + +String String::remove_chars(const char *p_chars) const { + return _remove_chars_common(*this, p_chars, strlen(p_chars)); +} + String String::substr(int p_from, int p_chars) const { if (p_chars == -1) { p_chars = length() - p_from; diff --git a/core/string/ustring.h b/core/string/ustring.h index 8670dc97ce8..294bb6028d5 100644 --- a/core/string/ustring.h +++ b/core/string/ustring.h @@ -432,6 +432,9 @@ public: String reverse() const; String insert(int p_at_pos, const String &p_string) const; String erase(int p_pos, int p_chars = 1) const; + String remove_char(char32_t p_what) const; + String remove_chars(const String &p_chars) const; + String remove_chars(const char *p_chars) const; String pad_decimals(int p_digits) const; String pad_zeros(int p_digits) const; String trim_prefix(const String &p_prefix) const; diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp index 730bf72b1d4..b43639bae70 100644 --- a/core/variant/variant_call.cpp +++ b/core/variant/variant_call.cpp @@ -1731,6 +1731,8 @@ static void _register_variant_builtin_methods_string() { bind_string_method(format, sarray("values", "placeholder"), varray("{_}")); bind_string_methodv(replace, static_cast(&String::replace), sarray("what", "forwhat"), varray()); bind_string_methodv(replacen, static_cast(&String::replacen), sarray("what", "forwhat"), varray()); + bind_string_method(remove_char, sarray("what"), varray()); + bind_string_methodv(remove_chars, static_cast(&String::remove_chars), sarray("chars"), varray()); bind_string_method(repeat, sarray("count"), varray()); bind_string_method(reverse, sarray(), varray()); bind_string_method(insert, sarray("position", "what"), varray()); diff --git a/doc/classes/String.xml b/doc/classes/String.xml index 68a7f673318..4f659a0a14b 100644 --- a/doc/classes/String.xml +++ b/doc/classes/String.xml @@ -763,6 +763,20 @@ [b]Example:[/b] [code]"this/is".path_join("path") == "this/is/path"[/code]. + + + + + Removes all occurrences of the Unicode character with code [param what]. Faster version of [method replace] when the key is only one character long and the replacement is [code]""[/code]. + + + + + + + Removes any occurrence of the characters in [param chars]. See also [method remove_char]. + + diff --git a/doc/classes/StringName.xml b/doc/classes/StringName.xml index 96972706e53..5b8c7a62891 100644 --- a/doc/classes/StringName.xml +++ b/doc/classes/StringName.xml @@ -671,6 +671,20 @@ [b]Example:[/b] [code]"this/is".path_join("path") == "this/is/path"[/code]. + + + + + Removes all occurrences of the Unicode character with code [param what]. Faster version of [method replace] when the key is only one character long and the replacement is [code]""[/code]. + + + + + + + Removes any occurrence of the characters in [param chars]. See also [method remove_char]. + + diff --git a/drivers/gles3/shader_gles3.cpp b/drivers/gles3/shader_gles3.cpp index 1537035c07b..50d89897c9c 100644 --- a/drivers/gles3/shader_gles3.cpp +++ b/drivers/gles3/shader_gles3.cpp @@ -73,7 +73,7 @@ void ShaderGLES3::_add_stage(const char *p_code, StageType p_stage_type) { } else if (l.begins_with("#CODE")) { chunk.type = StageTemplate::Chunk::TYPE_CODE; push_chunk = true; - chunk.code = l.replace_first("#CODE", String()).replace(":", "").strip_edges().to_upper(); + chunk.code = l.replace_first("#CODE", String()).remove_char(':').strip_edges().to_upper(); } else { text += l + "\n"; } diff --git a/editor/doc_tools.cpp b/editor/doc_tools.cpp index 02babe6a068..a3e5d775f1a 100644 --- a/editor/doc_tools.cpp +++ b/editor/doc_tools.cpp @@ -1632,7 +1632,7 @@ Error DocTools::save_classes(const String &p_default_path, const HashMap f = FileAccess::open(save_file, FileAccess::WRITE, &err); ERR_CONTINUE_MSG(err != OK, "Can't write doc file: " + save_file + "."); diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp index 8530cf8d8a4..ca16f84da94 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -1906,7 +1906,7 @@ void EditorHelp::_update_doc() { _push_code_font(); if (constant.value.begins_with("Color(") && constant.value.ends_with(")")) { - String stripped = constant.value.replace(" ", "").replace("Color(", "").replace(")", ""); + String stripped = constant.value.remove_char(' ').replace("Color(", "").remove_char(')'); PackedFloat64Array color = stripped.split_floats(","); if (color.size() >= 3) { class_desc->push_color(Color(color[0], color[1], color[2])); @@ -2439,7 +2439,7 @@ static void _add_text_to_rt(const String &p_bbcode, RichTextLabel *p_rt, const C const Color kbd_bg_color = p_owner_node->get_theme_color(SNAME("kbd_bg_color"), SNAME("EditorHelp")); const Color param_bg_color = p_owner_node->get_theme_color(SNAME("param_bg_color"), SNAME("EditorHelp")); - String bbcode = p_bbcode.dedent().replace("\t", "").replace("\r", "").strip_edges(); + String bbcode = p_bbcode.dedent().remove_chars("\t\r").strip_edges(); // Select the correct code examples. switch ((int)EDITOR_GET("text_editor/help/class_reference_examples")) { diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 02fbfc0908d..9d006199047 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -3291,7 +3291,7 @@ void EditorNode::_request_screenshot() { } void EditorNode::_screenshot(bool p_use_utc) { - String name = "editor_screenshot_" + Time::get_singleton()->get_datetime_string_from_system(p_use_utc).replace(":", "") + ".png"; + String name = "editor_screenshot_" + Time::get_singleton()->get_datetime_string_from_system(p_use_utc).remove_char(':') + ".png"; NodePath path = String("user://") + name; _save_screenshot(path); if (EDITOR_GET("interface/editor/automatically_open_screenshots")) { @@ -5070,8 +5070,8 @@ String EditorNode::_get_system_info() const { String display_session_type; #ifdef LINUXBSD_ENABLED - // `replace` is necessary, because `capitalize` introduces a whitespace between "x" and "11". - display_session_type = OS::get_singleton()->get_environment("XDG_SESSION_TYPE").capitalize().replace(" ", ""); + // `remove_char` is necessary, because `capitalize` introduces a whitespace between "x" and "11". + display_session_type = OS::get_singleton()->get_environment("XDG_SESSION_TYPE").capitalize().remove_char(' '); #endif // LINUXBSD_ENABLED String driver_name = OS::get_singleton()->get_current_rendering_driver_name().to_lower(); String rendering_method = OS::get_singleton()->get_current_rendering_method().to_lower(); @@ -5143,8 +5143,8 @@ String EditorNode::_get_system_info() const { String display_driver_window_mode; #ifdef LINUXBSD_ENABLED - // `replace` is necessary, because `capitalize` introduces a whitespace between "x" and "11". - display_driver_window_mode = DisplayServer::get_singleton()->get_name().capitalize().replace(" ", "") + " display driver"; + // `remove_char` is necessary, because `capitalize` introduces a whitespace between "x" and "11". + display_driver_window_mode = DisplayServer::get_singleton()->get_name().capitalize().remove_char(' ') + " display driver"; #endif // LINUXBSD_ENABLED if (!display_driver_window_mode.is_empty()) { display_driver_window_mode += ", "; diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp index d15ec2627d9..dd89a191936 100644 --- a/editor/editor_properties_array_dict.cpp +++ b/editor/editor_properties_array_dict.cpp @@ -401,7 +401,7 @@ void EditorPropertyArray::update_property() { } if (preview_value) { - String ctr_str = array.get_construct_string().trim_prefix(array_type_name + "(").trim_suffix(")").replace("\n", ""); + String ctr_str = array.get_construct_string().trim_prefix(array_type_name + "(").trim_suffix(")").remove_char('\n'); if (array_type == Variant::ARRAY && subtype != Variant::NIL) { int type_end = ctr_str.find("]("); if (type_end > 0) { @@ -1174,7 +1174,7 @@ void EditorPropertyDictionary::update_property() { object->set_dict(updated_val); if (preview_value) { - String ctr_str = updated_val.get_construct_string().replace("\n", ""); + String ctr_str = updated_val.get_construct_string().remove_char('\n'); if (key_subtype != Variant::NIL || value_subtype != Variant::NIL) { int type_end = ctr_str.find("]("); if (type_end > 0) { diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index dd53199739a..4929ec72795 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -2249,7 +2249,7 @@ void ScriptTextEditor::_text_edit_gui_input(const Ref &ev) { switch (expression_pattern) { case RGBA_PARAMETER: { color_args = line.substr(begin, end - begin); - String stripped = color_args.replace(" ", "").replace("\t", "").replace("(", "").replace(")", ""); + String stripped = color_args.remove_chars(" \t()"); PackedFloat64Array color = stripped.split_floats(","); if (color.size() > 2) { float alpha = color.size() > 3 ? color[3] : 1.0f; @@ -2261,7 +2261,7 @@ void ScriptTextEditor::_text_edit_gui_input(const Ref &ev) { end = line.length(); } color_args = line.substr(begin, end - begin); - const String color_name = color_args.replace(" ", "").replace("\t", "").replace(".", ""); + const String color_name = color_args.remove_chars(" \t."); const int color_index = Color::find_named_color(color_name); if (0 <= color_index) { const Color color_constant = Color::get_named_color(color_index); diff --git a/editor/project_converter_3_to_4.cpp b/editor/project_converter_3_to_4.cpp index e781baea4e5..8f3cb1d3e2a 100644 --- a/editor/project_converter_3_to_4.cpp +++ b/editor/project_converter_3_to_4.cpp @@ -1990,9 +1990,9 @@ void ProjectConverter3To4::process_gdscript_line(String &line, const RegExContai Vector parts = parse_arguments(line.substr(start, end)); if (parts.size() == 2) { if (builtin) { - line = line.substr(0, start) + "await " + parts[0] + "." + parts[1].replace("\\\"", "").replace("\\'", "").replace(" ", "") + line.substr(end + start); + line = line.substr(0, start) + "await " + parts[0] + "." + parts[1].replace("\\\"", "").replace("\\'", "").remove_char(' ') + line.substr(end + start); } else { - line = line.substr(0, start) + "await " + parts[0] + "." + parts[1].replace("\"", "").replace("\'", "").replace(" ", "") + line.substr(end + start); + line = line.substr(0, start) + "await " + parts[0] + "." + parts[1].remove_chars("\"' ") + line.substr(end + start); } } } @@ -2920,7 +2920,7 @@ String ProjectConverter3To4::line_formatter(int current_line, String from, Strin from = from.strip_escapes(); to = to.strip_escapes(); - line = line.replace("\r", "").replace("\n", "").strip_edges(); + line = line.remove_chars("\r\n").strip_edges(); return vformat("Line(%d), %s -> %s - LINE \"\"\" %s \"\"\"", current_line, from, to, line); } @@ -2935,8 +2935,8 @@ String ProjectConverter3To4::simple_line_formatter(int current_line, String old_ new_line = new_line.substr(0, 997) + "..."; } - old_line = old_line.replace("\r", "").replace("\n", "").strip_edges(); - new_line = new_line.replace("\r", "").replace("\n", "").strip_edges(); + old_line = old_line.remove_chars("\r\n").strip_edges(); + new_line = new_line.remove_chars("\r\n").strip_edges(); return vformat("Line (%d) - FULL LINES - \"\"\" %s \"\"\" =====> \"\"\" %s \"\"\"", current_line, old_line, new_line); } diff --git a/editor/property_selector.cpp b/editor/property_selector.cpp index 2ae3dc5d10c..de913a116ed 100644 --- a/editor/property_selector.cpp +++ b/editor/property_selector.cpp @@ -226,7 +226,7 @@ void PropertySelector::_update_search() { Ref icon; script_methods = false; - String rep = mi.name.replace("*", ""); + String rep = mi.name.remove_char('*'); if (mi.name == "*Script Methods") { icon = search_options->get_editor_theme_icon(SNAME("Script")); script_methods = true; diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp index b662b3eb299..4b723b6c43a 100644 --- a/modules/gdscript/gdscript_tokenizer.cpp +++ b/modules/gdscript/gdscript_tokenizer.cpp @@ -863,7 +863,7 @@ GDScriptTokenizer::Token GDScriptTokenizerText::number() { // Create a string with the whole number. int len = _current - _start; - String number = String(_start, len).replace("_", ""); + String number = String(_start, len).remove_char('_'); // Convert to the appropriate literal type. if (base == 16) { diff --git a/modules/gdscript/language_server/gdscript_workspace.cpp b/modules/gdscript/language_server/gdscript_workspace.cpp index ed58588f056..1608ef0988a 100644 --- a/modules/gdscript/language_server/gdscript_workspace.cpp +++ b/modules/gdscript/language_server/gdscript_workspace.cpp @@ -695,7 +695,7 @@ const lsp::DocumentSymbol *GDScriptWorkspace::resolve_symbol(const lsp::TextDocu } else { ScriptLanguage::LookupResult ret; - if (symbol_identifier == "new" && parser->get_lines()[p_doc_pos.position.line].replace(" ", "").replace("\t", "").contains("new(")) { + if (symbol_identifier == "new" && parser->get_lines()[p_doc_pos.position.line].remove_chars(" \t").contains("new(")) { symbol_identifier = "_init"; } if (OK == GDScriptLanguage::get_singleton()->lookup_code(parser->get_text_for_lookup_symbol(pos, symbol_identifier, p_func_required), symbol_identifier, path, nullptr, ret)) { diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index eef64e45b9f..27280ac98f9 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -172,8 +172,7 @@ static String fix_doc_description(const String &p_bbcode) { // This seems to be the correct way to do this. It's the same EditorHelp does. return p_bbcode.dedent() - .replace("\t", "") - .replace("\r", "") + .remove_chars("\t\r") .strip_edges(); } diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h index aab595c57eb..84bec5c646c 100644 --- a/modules/text_server_adv/text_server_adv.h +++ b/modules/text_server_adv/text_server_adv.h @@ -404,7 +404,7 @@ class TextServerAdvanced : public TextServerExtension { _FORCE_INLINE_ Variant::Type _get_tag_type(int64_t p_tag) const; _FORCE_INLINE_ bool _get_tag_hidden(int64_t p_tag) const; _FORCE_INLINE_ int _font_get_weight_by_name(const String &p_sty_name) const { - String sty_name = p_sty_name.replace(" ", "").replace("-", ""); + String sty_name = p_sty_name.remove_chars(" -"); if (sty_name.contains("thin") || sty_name.contains("hairline")) { return 100; } else if (sty_name.contains("extralight") || sty_name.contains("ultralight")) { @@ -431,7 +431,7 @@ class TextServerAdvanced : public TextServerExtension { return 400; } _FORCE_INLINE_ int _font_get_stretch_by_name(const String &p_sty_name) const { - String sty_name = p_sty_name.replace(" ", "").replace("-", ""); + String sty_name = p_sty_name.remove_chars(" -"); if (sty_name.contains("ultracondensed")) { return 50; } else if (sty_name.contains("extracondensed")) { diff --git a/modules/text_server_fb/text_server_fb.h b/modules/text_server_fb/text_server_fb.h index 2fc755cedee..2c7a27da6a5 100644 --- a/modules/text_server_fb/text_server_fb.h +++ b/modules/text_server_fb/text_server_fb.h @@ -341,7 +341,7 @@ class TextServerFallback : public TextServerExtension { } _FORCE_INLINE_ int _font_get_weight_by_name(const String &p_sty_name) const { - String sty_name = p_sty_name.replace(" ", "").replace("-", ""); + String sty_name = p_sty_name.remove_chars(" -"); if (sty_name.contains("thin") || sty_name.contains("hairline")) { return 100; } else if (sty_name.contains("extralight") || sty_name.contains("ultralight")) { @@ -368,7 +368,7 @@ class TextServerFallback : public TextServerExtension { return 400; } _FORCE_INLINE_ int _font_get_stretch_by_name(const String &p_sty_name) const { - String sty_name = p_sty_name.replace(" ", "").replace("-", ""); + String sty_name = p_sty_name.remove_chars(" -"); if (sty_name.contains("ultracondensed")) { return 50; } else if (sty_name.contains("extracondensed")) { diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp index f33edc2dd63..cd6943c470b 100644 --- a/platform/android/export/export_plugin.cpp +++ b/platform/android/export/export_plugin.cpp @@ -396,8 +396,7 @@ void EditorExportPlatformAndroid::_check_for_changes_poll_thread(void *ud) { // its format is "[property]: [value]" so changed it as like build.prop String p = props[j]; p = p.replace("]: ", "="); - p = p.replace("[", ""); - p = p.replace("]", ""); + p = p.remove_chars("[]"); if (p.begins_with("ro.product.model=")) { device = p.get_slicec('=', 1).strip_edges(); diff --git a/platform/linuxbsd/freedesktop_portal_desktop.cpp b/platform/linuxbsd/freedesktop_portal_desktop.cpp index 2a90a5f4c9b..dfac5e37767 100644 --- a/platform/linuxbsd/freedesktop_portal_desktop.cpp +++ b/platform/linuxbsd/freedesktop_portal_desktop.cpp @@ -505,7 +505,7 @@ Error FreeDesktopPortalDesktop::file_dialog_show(DisplayServer::WindowID p_windo String dbus_unique_name = String::utf8(dbus_bus_get_unique_name(monitor_connection)); String token = String::hex_encode_buffer(uuid, 64); - String path = vformat("/org/freedesktop/portal/desktop/request/%s/%s", dbus_unique_name.replace(".", "_").replace(":", ""), token); + String path = vformat("/org/freedesktop/portal/desktop/request/%s/%s", dbus_unique_name.replace(".", "_").remove_char(':'), token); fd.path = path; fd.filter = vformat("type='signal',sender='org.freedesktop.portal.Desktop',path='%s',interface='org.freedesktop.portal.Request',member='Response',destination='%s'", path, dbus_unique_name); diff --git a/platform/linuxbsd/os_linuxbsd.cpp b/platform/linuxbsd/os_linuxbsd.cpp index d597c92923b..62359a3bba8 100644 --- a/platform/linuxbsd/os_linuxbsd.cpp +++ b/platform/linuxbsd/os_linuxbsd.cpp @@ -173,7 +173,7 @@ String OS_LinuxBSD::get_unique_id() const { memset(buf, 0, sizeof(buf)); size_t len = sizeof(buf) - 1; if (sysctl(mib, 2, buf, &len, 0x0, 0) != -1) { - machine_id = String::utf8(buf).replace("-", ""); + machine_id = String::utf8(buf).remove_char('-'); } #else Ref f = FileAccess::open("/etc/machine-id", FileAccess::READ); diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index ef2e69ddc6b..50fa4bd6c3a 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -132,7 +132,7 @@ static String format_error_message(DWORD id) { LocalFree(messageBuffer); - return msg.replace("\r", "").replace("\n", ""); + return msg.remove_chars("\r\n"); } void RedirectStream(const char *p_file_name, const char *p_mode, FILE *p_cpp_stream, const DWORD p_std_handle) { diff --git a/scene/3d/xr/xr_face_modifier_3d.cpp b/scene/3d/xr/xr_face_modifier_3d.cpp index a176c3c37db..ff2b9ace858 100644 --- a/scene/3d/xr/xr_face_modifier_3d.cpp +++ b/scene/3d/xr/xr_face_modifier_3d.cpp @@ -341,7 +341,7 @@ static int find_face_blend_shape(const StringName &p_name) { }; // Convert the name to lower-case and strip non-alphanumeric characters. - const String name = String(p_name).to_lower().replace("_", ""); + const String name = String(p_name).to_lower().remove_char('_'); // Iterate through the blend map. for (const blend_map_entry &entry : blend_map) { diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp index 9011a70fdcf..ff3b97a292b 100644 --- a/scene/gui/code_edit.cpp +++ b/scene/gui/code_edit.cpp @@ -137,7 +137,7 @@ void CodeEdit::_notification(int p_what) { Point2 round_ofs = hint_ofs + theme_cache.code_hint_style->get_offset() + Vector2(0, theme_cache.font->get_ascent(theme_cache.font_size) + font_height * i + yofs); round_ofs = round_ofs.round(); - draw_string(theme_cache.font, round_ofs, line.replace(String::chr(0xFFFF), ""), HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size, theme_cache.code_hint_color); + draw_string(theme_cache.font, round_ofs, line.remove_char(0xFFFF), HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size, theme_cache.code_hint_color); if (end > 0) { // Draw an underline for the currently edited function parameter. const Vector2 b = hint_ofs + theme_cache.code_hint_style->get_offset() + Vector2(begin, font_height + font_height * i + yofs); diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index bef3f6450c5..811569bcc1b 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -721,7 +721,7 @@ void ColorPicker::_update_presets() { } if (palette_edited) { - palette_name->set_text(vformat("%s*", palette_name->get_text().replace("*", ""))); + palette_name->set_text(vformat("%s*", palette_name->get_text().remove_char('*'))); palette_name->set_tooltip_text(ETR("The changes to this palette have not been saved to a file.")); } } @@ -1086,7 +1086,7 @@ void ColorPicker::erase_preset(const Color &p_color) { } } - palette_name->set_text(vformat("%s*", palette_name->get_text().replace("*", ""))); + palette_name->set_text(vformat("%s*", palette_name->get_text().remove_char('*'))); palette_name->set_tooltip_text(ETR("The changes to this palette have not been saved to a file.")); if (presets.is_empty()) { palette_name->set_text(""); diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 00018102be9..a2b29ff6a3e 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -7337,7 +7337,7 @@ void TextEdit::_paste_internal(int p_caret) { } // Paste a full line. Ignore '\r' characters that may have been added to the clipboard by the OS. - if (get_caret_count() == 1 && !has_selection(0) && !cut_copy_line.is_empty() && cut_copy_line == clipboard.replace("\r", "")) { + if (get_caret_count() == 1 && !has_selection(0) && !cut_copy_line.is_empty() && cut_copy_line == clipboard.remove_char('\r')) { insert_text(clipboard, get_caret_line(), 0); return; } @@ -8584,7 +8584,7 @@ void TextEdit::_base_insert_text(int p_line, int p_char, const String &p_text, i ERR_FAIL_COND(p_char < 0); /* STEP 1: Remove \r from source text and separate in substrings. */ - const String text_to_insert = p_text.replace("\r", ""); + const String text_to_insert = p_text.remove_char('\r'); Vector substrings = text_to_insert.split("\n"); // Is this just a new empty line? diff --git a/servers/rendering/renderer_rd/shader_rd.cpp b/servers/rendering/renderer_rd/shader_rd.cpp index ed9428a6a35..b35d81c54cb 100644 --- a/servers/rendering/renderer_rd/shader_rd.cpp +++ b/servers/rendering/renderer_rd/shader_rd.cpp @@ -76,7 +76,7 @@ void ShaderRD::_add_stage(const char *p_code, StageType p_stage_type) { } else if (l.begins_with("#CODE")) { chunk.type = StageTemplate::Chunk::TYPE_CODE; push_chunk = true; - chunk.code = l.replace_first("#CODE", String()).replace(":", "").strip_edges().to_upper(); + chunk.code = l.replace_first("#CODE", String()).remove_char(':').strip_edges().to_upper(); } else if (l.begins_with("#include ")) { String include_file = l.replace("#include ", "").strip_edges(); if (include_file[0] == '"') { diff --git a/tests/core/string/test_string.h b/tests/core/string/test_string.h index 35f693a3cee..ee7471afdb5 100644 --- a/tests/core/string/test_string.h +++ b/tests/core/string/test_string.h @@ -466,6 +466,23 @@ TEST_CASE("[String] Erasing") { CHECK(s == "Josephine is such a girl!"); } +TEST_CASE("[String] remove_char") { + String s = "Banana"; + CHECK(s.remove_char('a') == "Bnn"); + CHECK(s.remove_char('\0') == "Banana"); + CHECK(s.remove_char('x') == "Banana"); +} + +TEST_CASE("[String] remove_chars") { + String s = "Banana"; + CHECK(s.remove_chars("Ba") == "nn"); + CHECK(s.remove_chars(String("Ba")) == "nn"); + CHECK(s.remove_chars("") == "Banana"); + CHECK(s.remove_chars(String()) == "Banana"); + CHECK(s.remove_chars("xy") == "Banana"); + CHECK(s.remove_chars(String("xy")) == "Banana"); +} + TEST_CASE("[String] Number to string") { CHECK(String::num(0) == "0.0"); // The method takes double, so always add zeros. CHECK(String::num(0.0) == "0.0");