论坛首页 Java企业应用论坛

一个诡异的字符串替换BUG

浏览 6029 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2014-03-08   最后修改:2014-03-08
将字符串中的数字替换成$D

希望将以下字符串中的数字替换为$D字符
引用

   ab1cd2


我们知道String有3个用于字符替换的方法,分别是:
  • String replace(CharSequence target, CharSequence replacement):将字符串中出现的target替换成replacement;
  • String replaceAll(String regex, String replacement):regex是一个正则表达式,将字符串中匹配的子字符串替换为replacement;
  • String replaceFirst(String regex, String replacement):和replaceAll(..)类似,只不过只替换第一个出现的地方。


由于我们希望所有替换,因此使用如下方法:
代码1:StringReplaceTest
public class StringReplaceTest {
    @Test
    public void testReplace(){
        String str = "ab1cd2";
        System.out.println(str.replaceAll(str, "$D"));
    }
}


小小代码现诡异异常

运行StringReplaceTest,控制台却没有返回正确的结果,而是抛出如下的异常:
引用

java.lang.IllegalArgumentException: Illegal group reference
at java.util.regex.Matcher.appendReplacement(Matcher.java:713)
at java.util.regex.Matcher.replaceAll(Matcher.java:813)
at java.lang.String.replaceAll(String.java:2189)
at com.hsit.euler.qform.engine.jdbc.StringReplaceTest.testReplace(StringReplaceTest.java:17)


比较诡异吧,难道是JDK的BUG???

剥丝入茧,原来如此

其实String的replaceAll()及replaceFirst()方法内部都是调用java.util.regex.Matcher的String replaceAll(String replacement)方法的。让我们把刚才的诧异放在一边,好好看下这个方法的Javadoc,掐头去尾,主要是这段:
引用

     * <p> Note that backslashes (<tt>\</tt>) and dollar signs (<tt>$</tt>) in
     * the replacement string may cause the results to be different than if it
     * were being treated as a literal replacement string. Dollar signs may be
     * treated as references to captured subsequences as described above, and
     * backslashes are used to escape literal characters in the replacement
     * string.


原来是我们的替换目标串中包含了$这个特殊的字符,因为替换串使用这个引用正则表达式匹配的组,$0代表匹配项,$1代表第1个匹配分组,$1代表第2个匹配分组--终于真相大白了,是我们闯了雷区了

来看一个例子加深一个印象:

    @Test
    public void testReplace2(){
        String str = "刘备是张飞的小弟";
        System.out.println(str.replaceAll("(刘备)是(张飞)", "$2是$1"));
        //=>张飞是刘备的小弟
    }


李鬼出来,李逹进去
李鬼现形,处理起来自然简单:
    @Test
    public void testReplace(){
        String str = "ab1cd2";
        System.out.println(str.replaceAll(str, "\\$D"));
        //=>ab$Dcd$D
    }



小评一下

如果JDK可以再分析一下$,将$N即N是数字时才对其进行特殊处理,否则就不当成特殊字符,是不是更好一些呢?

也许这样并不好,必须这样造成Matcher方法为了这个小概率事件做很多复杂的检查,结果是得不偿失的。还是遇到特殊字符报异常,让开发者去处理更好些,这是28原来取舍得当的一个API设计。既然$是特殊字符,开发者绕过即可。
   发表时间:2014-03-12  
请问意义何在
0 请登录后投票
   发表时间:2014-03-13  
rex0654335 写道
请问意义何在

+1
0 请登录后投票
   发表时间:2014-03-14  
rex0654335 写道
请问意义何在

+2
楼下跟上
0 请登录后投票
   发表时间:2014-03-14  
挺好的。看来API要仔细看啊
0 请登录后投票
   发表时间:2014-03-14  
rex0654335 写道
请问意义何在

+1
0 请登录后投票
   发表时间:2014-03-14   最后修改:2014-03-14
意义何在是指啥呢?当然方法要放在具体上下文中才有意义,我故意将上下文去掉了,重点突出这个API容易出错的情况,所以无需关注我这段代码有什么具体意义啊。
0 请登录后投票
   发表时间:2014-03-14  
public static void main(String args[]){ 
        String str = "ab1cd2"; 
        System.out.println(str.replaceAll("\\d", Matcher.quoteReplacement("$D"))); 
    }
0 请登录后投票
   发表时间:2014-03-14  
goldenfish1919 写道
public static void main(String args[]){ 
        String str = "ab1cd2"; 
        System.out.println(str.replaceAll("\\d", Matcher.quoteReplacement("$D"))); 
    }

     +1
0 请登录后投票
   发表时间:2014-03-25  
$符合本身就是正则表达式里面的特殊符合,string自带的方法替换本身就支持正则表达式。不挂才怪
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics