JAVA

Java replace和replaceAll

问题


最近在查线上问题的时候,发现日志里面挺多这种PatternSyntaxException错误的。从日志中可以清楚的知道,出问题的地方就是调用了String的replaceAll方法。等等,这个不就是个普通的替换方法嘛?难道是我用的姿势不对?话不多说,赶紧看一波源码,将bug修复

原因分析以及解决方案


在大多数人的认知里,replace方法是替换符合的单个字符串,而replaceAll是替换所有符合的字符串。瞄了一下源码,真的打脸了。

源码分析

 /**
     * Replaces each substring of this string that matches the literal target
     * sequence with the specified literal replacement sequence. The
     * replacement proceeds from the beginning of the string to the end, for
     * example, replacing "aa" with "b" in the string "aaa" will result in
     * "ba" rather than "ab".
      翻译:用指定的文字序列替换与目标文字序列中匹配的每个子字符串
     */
    public String replace(CharSequence target, CharSequence replacement) {
        return Pattern.compile(target.toString(), Pattern.LITERAL).matcher(
                this).replaceAll(Matcher.quoteReplacement(replacement.toString()));
    }


    /**
     * Replaces each substring of this string that matches the given <a
     * href="../util/regex/Pattern.html#sum">regular expression</a> with the
     * given replacement.
      翻译:将目标文字序列中与给定的正则表达式匹配的每个字符串替换
     */
    public String replaceAll(String regex, String replacement) {
        return Pattern.compile(regex).matcher(this).replaceAll(replacement);
    }

replace和replaceAll是JAVA中常用的替换字符的方法

  • public String replace(char oldChar, char newChar)         在字符串中用newChar字符替代oldChar字符,返回一个新的字符串
  • public String replaceAll(String regex,String replacement)使用给定的 replacement 字符串替换此字符串匹配给定的正则表达式的每个子字符串。

区别:

  • replace的参数是char和CharSequence,即可以支持字符的替换,也支持字符串的替换(CharSequence即字符串序列的意思,说白了也就是字符串);
  • replaceAll的参数是regex,即基于正则表达式的替换,比如,可以通过replaceAll(“\\d“, “*”)把一个字符串所有的数字字符都换成星号;

相同点:

都是全部替换,底层都是调用了Matcher.replaceAll()方法,即把源字符串中的某一字符或字符串全部换成指定的字符或字符串,如果只想替换第一次出现的,可以使用replaceFirst(),这个方法也是基于规则表达式的替换,但与replaceAll()不同的是,只替换第一次出现的字符串;

另外,如果replaceAll()和replaceFirst()所用的参数据不是基于规则表达式的,则与replace()替换字符串的效果是一样的,即这两者也支持字符串的操作;

还有一点注意::执行了替换操作后,源字符串的内容是没有发生改变的。

原因


由于replacAll()方法,被替换的参数支持正则表达式,而我传入的值存在特殊字符,没有进行转义,导致解析失败。

解决方案


业务需求是将指定的字符串直接全部替换掉,所以只需将replaceAll()方法更换为replace()方法即可,解决问题。

代码论证


看一个例子

        String word1 = "test.test.test";
        String word2 = "test.test.test";
        String word3 = "test.test.test";

        String word11 = word1.replace(".","@");  //test@test@test
        String word22 = word2.replaceAll(".","@"); //@@@@@@@@@@@@@@
        String word33 = word3.replaceFirst(".","@");//@est.test.test

        System.out.println("word11: " + word11 + ";word22:" + word22 + ";word33: " + word33);
        System.out.println("word1: " + word1 + ";word2:" + word2 + ";word3: " + word3);

word11: test@test@test;word22:@@@@@@@@@@@@@@;word33: @est.test.test
word1: test.test.test;word2:test.test.test;word3: test.test.test

由于”.”属于正则表达式的符号(匹配除换行符 \n 之外的任何单字符),所以replaceAll()执行正则替换;此外,我们可以注意到,replaceFirst()这个方法,其实这个方法跟replaceAll()实现是一样的,执行正则替换,只是该方法只替换符合条件的第一个字符或者字符串

总结


  • replace()方法是将目标字符串中完全匹配的指定的字符或者字符串全部替换
  • replaceAll()和replaceFirst()方法,则是基于正则表达式的替换,比如,通过replace(“\d”,”@”)是把一个字符串中的所有数字替换为@
  • 从上面的例子中,执行替换操作之后,其实是返回一个新对象,源字符串的内容则是不变的。