程序员

Java replaceAll 方法的「天坑」

2016-11-30  本文已影响284人  Cyandev

好吧,我承认我可能标题党了,其实大部分所谓的「坑」都是由于自己的无知所造成的。首先来说说我遇到的问题吧,我的一个项目里需要在 Java 环境下从服务器获取一段 JSON,然后拼接成一个 JavaScript 函数调用语句,传递给 WebView 中的页面去执行,由于是拼接的语句,所以 JSON 中的引号我们还需要进行一次转义,于是我理所当然地写下了这行代码:

String escaped = json.replaceAll("\"", "\\\"");

乍一看,貌似真没什么问题,但当我执行的时候,我发现它根本就 NOT WORKING!!于是我向 GoogleStackOverflow 求救,他们告诉我需要这样写:

String escaped = json.replaceAll("\"", "\\\\\"");

WTF??为什么会有五个反斜杠?但是时间紧迫我也没有深入研究这个问题,只是在知乎留下了一个问题:「Java 引号为什么要这样转义?」

后来我看到了知友的回答,说实话,看到的一瞬间我就恍然大悟了,他的回答也没再看完。

所以是什么问题呢?咱们看看 replaceAll 这个方法的文档:

Replaces each substring of this string that matches the given <a href="#">regular expression</a> with the given replacement.
<p> An invocation of this method of the form
<i>str</i><tt>.replaceAll(</tt><i>regex</i><tt>,</tt> <i>repl</i><tt>)</tt>
yields exactly the same result as the expression
...
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; see
{@link java.util.regex.Matcher#replaceAll Matcher.replaceAll}.
Use {@link java.util.regex.Matcher#quoteReplacement} to suppress the special
meaning of these characters, if desired.

明白了吗,replaceAll 的第一个参数接受一个正则表达式,这个我们应该都能理解,但有的时候我们像在被替换的内容中引用这个正则所捕获到的内容。试举一例,假设有一个字符串 "中英文mix在一起",我们想要将 “mix” 这个单词和中文文字之间用空格空开(我们都知道这是最规范的写法),那么在替换时我们肯定还需要引用到被找到的不规范字符串子串,那么替换内容就是:
[空格] + mix + [空格]

那么在 Java 中如何引用被捕获的子串呢?那就是用 $ 修饰符。我们可以尝试一下:

String s = "中英文mix在一起";
s = s.replaceAll("((?<=[^\\x00-\\xff])[a-zA-Z]+)|([a-zA-Z]+(?=[^\\x00-\\xff]))", " $0 ");
System.out.println(s);

<strong>这里有个小 tip</strong>
如果你使用 IntelliJ IDEA 的话,把正则表达式在记事本中写好再复制回 IDE,它会帮你自动转义,还是很方便的。

这段程序执行的结果就是:

中英文 mix 在一起

Process finished with exit code 0

符合我们的预期。到这里我们应该能凭直觉得出,如果要将文本替换成 $ 的话,我们就还需要转义,也就是写成 \$,同理,如果我们要使用 \ 的话,也需要转义,也就是写成 \\,那么文章一开头的那个例子中,把 " 替换成 \" 的话,第二个参数我们就需要写成 \\",我们在 IDE 外部复制它,再粘贴到代码中,IDE 帮我们再作一次转义就得到 \\\\\" 了。

理顺一下:

最后就是五个 \ 了。就是这么简单,希望你还没晕 ;-)

上一篇下一篇

猜你喜欢

热点阅读