假设有这样一个字符串:
[{"startDate":"2018-01-03 00:00:00","reason":"a\"","endDate":"2018-01-24 00:00:00","updateDate":"","version":"","id":"9999999","msgType":"","createDate":"2018-01-03 11:50:44"}]
要将其中reason字段的值中的“\"”替换为“--”,使用String.replaceAll(String regex, String replacement)函数,这应该这么做呢?
一开始,我的代码如下:
输出结果:
[{--startDate--:--2018-01-03 00:00:00--,--reason--:--a----,--endDate--:--2018-01-24 00:00:00--,--updateDate--:----,--version--:----,--id--:--9999999--,--msgType--:----,--createDate--:--2018-01-03 11:50:44--}]
可以看到,该程序把所有的“"”都替换成了“--”,输入的正则表达式参数明明是“\\"”,经转义之后是“\"”,为什么会匹配所有的“"”呢?
接下来,把正则表达式参数改为“\\\\"”。
输出结果:
[{"startDate":"2018-01-03 00:00:00","reason":"a--","endDate":"2018-01-24 00:00:00","updateDate":"","version":"","id":"9999999","msgType":"","createDate":"2018-01-03 11:50:44"}]
达到预期的结果。
通过上面两个程序,可以初步判断:java代码中字符串常量层面,将regex参数字面量“\\\\"”转义一遍,其值是“\\"”;在String.replaceAll函数中,对“\\"”进行正则表达式层面的转义得到“\"”。现在,让我们进入到JDK源码中,学习正则表达式层面的具体的转义过程。
String.replaceAll(String regex, String replacement)函数如下:
它共调用了三个函数,分别是:Pattern.compile(String regex),Pattern.matcher(CharSequence input)和Matcher.replaceAll(String replacement)。
1. 编译(解析)正则表达式,获得Pattern对象
Pattern.compile(String regex)函数如下:
它返回的是一个Pattern对象。Pattern的构造函数如下:
这个构造函数是private级别的,不能被其他类直接调用,只能通过Pattern类的compile(String regex)和compile(String regex, int flags)调用。该构造函数调用了compile(),对regex参数的处理就发生在这个函数里面。
Pattern.compile()函数如下:
其中,matchRoot = expr(lastAccept);获得正则表达式匹配根结点。
Pattern.expr(Node end)函数如下:
其中,Node node = sequence(end);
Pattern.sequence(Node end)函数如下:
该函数使用了带标签循环“LOOP:”。for ( ; ; )是一个死循环,int ch = peek();取正则表达式当前字符。
Pattern.peek()函数如下:
其中,temp[0]到temp[3]的值分别为92,92,92,34,即“\\"”转为整型数组。
Pattern.sequence(Node end)函数中,swith开关下,有“case '\':”。读取到第一个“\”后,执行ch = nextEscaped();,获取下一个字符。调用Pattern.unread();将游标cursor自减一,即重新指向第一个“\”,然后执行node = atom();。
Pattern.atom()函数如下:
因为游标cursor在Pattern.sequence(Node end)函数中又重新指向了第一个“\”,所以该函数还是会进入“case '\':”。同样地,执行ch = nextEscaped();,获取下一个字符;调用Pattern.unread();将游标cursor自减一,即重新指向第一个“\”。
ch = escape(false, first == 0);,该语句获取到转义后的真实字符,即第二个“\”。
Pattern.escape(boolean inclass, boolean create)函数如下:
这个函数就是解析正则表达式的地方。
int ch = skip();,该语句就是获取“\”之后的字符的地方。
Pattern.skip()函数如下:
Pattern.atom()函数在获取第二个“\”后,执行append(ch, first);将该字符存到Pattern对象的buffer数组中。
Pattern.atom()函数中的for循环,会在读取到temp的0时结束。在Pattern.compile()函数中,可以知道,temp数组长度比正则表达式长度大2,其最后两个元素的值都为0。最终,buffer有两个有效元素,分别是92(“\”)和34(“"”),后续元素全是0。
Pattern.compile(String regex)函数分析到此。

该Matcher对象关联了Pattern对象和需处理的String对象。
3. 替换字符串
Matcher.replaceAll(String replacement)函数如下:
[{"startDate":"2018-01-03 00:00:00","reason":"a--","endDate":"2018-01-24 00:00:00","updateDate":"","version":"","id":"9999999","msgType":"","createDate":"2018-01-03 11:50:44"}]
欢迎拍砖,谢谢!