问题
replaceAll/replaceFirst会特殊处理入参(要替换的目标值)中 \ 和 $ 这两个字符,而replace则把它们两个当成字符串处理?
【使用replaceAll/replaceFirst时,动态入参需要注意,避免入坑】
方法介绍
对String进行字符串替换,有下面三个方法可以使用
replace:直接把入参当字符串处理
replaceAll:会把入参当正则表达式处理
replaceFirst:会把入参当正则表达式处理
原因
当使用replaceAll/replaceFirst时
第一个入参为要替换的值可以为正则表达式,这个没有问题
第二个入参为替换后的值,为字符串,也符合我们之前的使用习惯,但是点进源码中,发现还是有差别的,实际上会对字符串中包含的 \ 和 $ 进行特殊处理(给 $ 这个符号增加功能)
位置:replaceAll/replaceFirst --> Matcher.appendReplacement(while循环中)
从上面代码中可以看出,按字符处理的时候,会对 \ 和 $ 这两个字符进行特殊处理(replacement为第二个入参传入的字符串)
\:如果字符为 \ 的时候,会取下一个字符【如果没有下一位,则抛异常】
$:如果字符为 $ 的时候,会进行特殊处理,即取后面的N个字符,这N个字符需要 { 开头,} 结尾,或者其他规则(规则自行百度)【如果没有下一位,则抛异常。不是 { 开头或 } 结尾,或者其他支持的规则,也抛异常】
\ 主要是为 $ 服务的,即 $ 前面加 \ 则不进行特殊处理,取下一位就是 $ 了
所以我们在传第二个入参目标值的时候,如果只是把 \ 和 $ 当成正常的字符串对待的话,需要加上转义字符 \ ,Matcher中也提供了quoteReplacement对这两个特殊字符进行特殊转换。如果使用replaceAll/replaceFirst,入参是动态的,可以将入参通过Matcher.quoteReplacement处理一下,这样就不用自己去判断了
例子:
// replaceFirst 与 replaceAll同理
System.out.println("abcd".replaceFirst("a", "\\g")); // gbcd
System.out.println("abcd".replaceFirst("a", Matcher.quoteReplacement("\\g"))); // \gbcd
System.out.println("abcd".replaceFirst("a", "$g")); // 抛异常,因为$后面没有特殊处理的规则
System.out.println("abcd".replaceFirst("a", Matcher.quoteReplacement("$g"))); // $gbcd
为什么replace就可以将入参都当成字符串处理?
通过下面代码截图可以看到,其实它也是调用了replaceAll,然后通过特殊处理,来当成字符串而已