String字符串替换的一个诡异问题

本文通过一个具体的例子探讨了在Java中使用String的replaceAll方法时遇到的问题,特别是当替换字符串包含特殊字符'$'时如何正确地进行转义。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

[b][size=medium]将字符串中的数字替换成$D[/size][/b]

希望将以下字符串中的数字替换为$D字符
[quote]
ab1cd2
[/quote]

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

由于我们希望所有替换,因此使用如下方法:
[b]代码1:StringReplaceTest[/b]

public class StringReplaceTest {
@Test
public void testReplace(){
String str = "ab1cd2";
System.out.println(str.replaceAll(str, "$D"));
}
}


[b][size=medium]小小代码现诡异异常[/size]
[/b]
运行StringReplaceTest,控制台却没有返回正确的结果,而是抛出如下的异常:
[quote]
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)
[/quote]

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

[b][size=medium]剥丝入茧,原来如此[/size][/b]

其实String的replaceAll()及replaceFirst()方法内部都是调用java.util.regex.Matcher的String replaceAll(String replacement)方法的。让我们把刚才的诧异放在一边,好好看下这个方法的Javadoc,掐头去尾,主要是这段:
[quote]
* <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.
[/quote]

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

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


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


[b][size=medium]李鬼出来,李逹进去[/size][/b]
李鬼现形,处理起来自然简单:

@Test
public void testReplace(){
String str = "ab1cd2";
System.out.println(str.replaceAll(str, "\\$D"));
//=>ab$Dcd$D
}



[b][size=medium]小评一下[/size][/b]

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

也许这样并不好,必须这样造成Matcher方法为了这个小概率事件做很多复杂的检查,结果是得不偿失的。还是遇到特殊字符报异常,让开发者去处理更好些,这是28原来取舍得当的一个API设计。既然$是特殊字符,开发者绕过即可。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值