如何在 Java 中替换不区分大小写的文字子字符串

Posted

技术标签:

【中文标题】如何在 Java 中替换不区分大小写的文字子字符串【英文标题】:How to replace case-insensitive literal substrings in Java 【发布时间】:2011-06-30 14:41:22 【问题描述】:

使用String中的replace(CharSequence target, CharSequence replacement)方法,如何让目标不区分大小写?

例如,它现在的工作方式:

String target = "FooBar";
target.replace("Foo", "") // would return "Bar"

String target = "fooBar";
target.replace("Foo", "") // would return "fooBar"

我怎样才能使它替换(或者如果有更合适的方法)不区分大小写,以便两个示例都返回“Bar”?

【问题讨论】:

【参考方案1】:

如果你不关心大小写,那么你可能返回全部大写都没关系:

target.toUpperCase().replace("FOO", "");

【讨论】:

如果您处理 á 等字符,您也可以将 Locale 传递给 toUpperCase(locale)。【参考方案2】:
String target = "FOOBar";
target = target.replaceAll("(?i)foo", "");
System.out.println(target);

输出:

Bar

值得一提的是,replaceAll 将第一个参数视为正则表达式模式,这可能会导致意外结果。要解决此问题,还请按照 cmets 中的建议使用 Pattern.quote

【讨论】:

如果目标包含 $ 或 á 之类的变音字符怎么办? 我的意思是两件事:1. "blÁÜ123".replaceAll("(?i)bláü") 不会替换任何东西。 2. "Sentence!End".replaceAll("(?i)Sentence.") 的替换可能超出预期。 你不能把字符串变成正则表达式匹配它这么简单。这通常是不正确的,它只适用于特定情况。 使用 Pattern.quote() 保护搜索字符串不被解释为正则表达式。这并没有解决上面列出的 unicode 怪癖,但对于基本字符集应该没问题。例如target.replaceAll("(?i)"+Pattern.quote("foo"), ""); 只是确保。如果字符串是“foo”,则不需要 Pattern.quote("foo") 对吗?只有当它更花哨的时候,对吧?【参考方案3】:

由于某些字符被保留,正则表达式管理起来相当复杂:例如,"foo.bar".replaceAll(".") 产生一个空字符串,因为点表示“任何东西” 如果您只想替换该点,则应指示为一个参数"\\."

更简单的解决方案是使用 StringBuilder 对象来搜索和替换文本。它需要两个:一个包含小写版本的文本,而第二个包含原始版本。对小写内容进行搜索,检测到的索引也将替换原始文本。

public class LowerCaseReplace 

    public static String replace(String source, String target, String replacement)
    
        StringBuilder sbSource = new StringBuilder(source);
        StringBuilder sbSourceLower = new StringBuilder(source.toLowerCase());
        String searchString = target.toLowerCase();

        int idx = 0;
        while((idx = sbSourceLower.indexOf(searchString, idx)) != -1) 
            sbSource.replace(idx, idx + searchString.length(), replacement);
            sbSourceLower.replace(idx, idx + searchString.length(), replacement);
            idx+= replacement.length();
        
        sbSourceLower.setLength(0);
        sbSourceLower.trimToSize();
        sbSourceLower = null;

        return sbSource.toString();
    


    public static void main(String[] args)
    
        System.out.println(replace("xXXxyyyXxxuuuuoooo", "xx", "**"));
        System.out.println(replace("FOoBaR", "bar", "*"));
    

【讨论】:

效果很好!请注意,“目标”不能为空。不再需要清除 sbSourceLower(不再需要)。 感谢简洁的解决方案,感谢@msteiger 的更正。我想知道为什么没有人为 Guava、Apache Commons 等任何著名的库添加类似的解决方案? 这是否比基于正则表达式的解决方案更好(在性能上)? 这个功能不错,通俗易懂【参考方案4】:

也许不像其他方法那样优雅,但它非常可靠且易于遵循,尤其是。适用于 Java 新手。让我了解 String 类的一件事是:它已经存在了很长时间,虽然它支持使用正则表达式进行全局替换和使用字符串进行全局替换(通过 CharSequences),但最后一个没有简单的布尔参数:“不区分大小写”。真的,您可能认为只需添加一个小开关,就可以避免缺少它给初学者带来的所有麻烦。现在在 JDK 7 上,String still 不支持这一点添加!

无论如何,我会停止抱怨。对于特别是 Java 新手,这里是你的剪切和粘贴 deus ex machina。正如我所说,它没有那么优雅,也不会为您赢得任何漂亮的编码奖,但它有效且可靠。任何 cmets,请随时贡献。 (是的,我知道,StringBuffer 可能是管理两个字符串突变行的更好选择,但交换技术很容易。)

public String replaceAll(String findtxt, String replacetxt, String str, 
        boolean isCaseInsensitive) 
    if (str == null) 
        return null;
    
    if (findtxt == null || findtxt.length() == 0) 
        return str;
    
    if (findtxt.length() > str.length()) 
        return str;
    
    int counter = 0;
    String thesubstr = "";
    while ((counter < str.length()) 
            && (str.substring(counter).length() >= findtxt.length())) 
        thesubstr = str.substring(counter, counter + findtxt.length());
        if (isCaseInsensitive) 
            if (thesubstr.equalsIgnoreCase(findtxt)) 
                str = str.substring(0, counter) + replacetxt 
                    + str.substring(counter + findtxt.length());
                // Failing to increment counter by replacetxt.length() leaves you open
                // to an infinite-replacement loop scenario: Go to replace "a" with "aa" but
                // increment counter by only 1 and you'll be replacing 'a's forever.
                counter += replacetxt.length();
             else 
                counter++; // No match so move on to the next character from
                           // which to check for a findtxt string match.
            
         else 
            if (thesubstr.equals(findtxt)) 
                str = str.substring(0, counter) + replacetxt 
                    + str.substring(counter + findtxt.length());
                counter += replacetxt.length();
             else 
                counter++;
            
        
    
    return str;

【讨论】:

这个方法非常慢,因为它的复杂度是 O(size_str * size_findtext)【参考方案5】:

我喜欢smas 的answer,它使用带有正则表达式的replaceAll。如果您要多次执行相同的替换,那么预编译正则表达式一次是有意义的:

import java.util.regex.Pattern;

public class Test  

    private static final Pattern fooPattern = Pattern.compile("(?i)foo");

    private static removeFoo(s)
        if (s != null) s = fooPattern.matcher(s).replaceAll("");
        return s;
    

    public static void main(String[] args) 
        System.out.println(removeFoo("FOOBar"));
    

【讨论】:

【参考方案6】:

对于非 Unicode 字符:

String result = Pattern.compile("(?i)препарат", 
Pattern.UNICODE_CASE).matcher(source).replaceAll("БАД");

【讨论】:

【参考方案7】:

org.apache.commons.lang3.StringUtils:

public static String replaceIgnoreCase(String text, 字符串搜索字符串, 字符串替换)

不区分大小写地替换另一个字符串中所有出现的字符串。

【讨论】:

【参考方案8】:

无需第三方库,只需简单:

    final String source = "FooBar";
    final String target = "Foo";
    final String replacement = "";
    final String result = Pattern.compile(target, Pattern.LITERAL | Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE).matcher(source)
.replaceAll(Matcher.quoteReplacement(replacement));

【讨论】:

【参考方案9】:
String newstring  = "";
String target2 = "fooBar";
newstring = target2.substring("foo".length()).trim();   
logger.debug("target2: ",newstring); 
// output: target2: Bar
    
String target3 = "FooBar";
newstring = target3.substring("foo".length()).trim();
logger.debug("target3: ",newstring); 
// output: target3: Bar

【讨论】:

以上是关于如何在 Java 中替换不区分大小写的文字子字符串的主要内容,如果未能解决你的问题,请参考以下文章

如何查看 Java 1.4 中的另一个字符串中是不是存在子字符串?

JavaScript中不区分大小写的字符串替换?

在 Pandas DataFrame 的列中查找并替换所有匹配但不区分大小写的字符串

只能输入文字,数字,大小写英文的js正则表达式.

JavaScript 正则表达式

JavaScript正则表达式修饰符