您的位置:首页 > 编程语言 > Java开发

Java替换字符串中的“\"”的处理方法(String.replaceAll的源码分析)

2018-01-05 16:09 906 查看
假设有这样一个字符串:

[{"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)函数分析到此。

2. 获取匹配器

Pattern.matcher(CharSequence input)函数如下:



该Matcher对象关联了Pattern对象和需处理的String对象。

3. 替换字符串

Matcher.replaceAll(String replacement)函数如下:

boolean result = find();,查找第一个能匹配正则表达式的地方并完成替换(若有匹配得上的地方)。

之后,在do...while循环中继续往后查找并替换,直到无匹配得上的地方。把字符串剩余部分接到替换后字符串后面,即可得到想要得到的字符串,即:



boolean result = find();,查找第一个能匹配正则表达式的地方并完成替换(若有匹配得上的地方)。

之后,在do...while循环中继续往后查找并替换,直到无匹配得上的地方。把字符串剩余部分接到替换后字符串后面,即可得到想要得到的字符串,即:

[{"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"}]
欢迎拍砖,谢谢!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: