美文网首页程序员
Java replaceAll 方法的「天坑」

Java replaceAll 方法的「天坑」

作者: Cyandev | 来源:发表于2016-11-30 22:29 被阅读284次

    好吧,我承认我可能标题党了,其实大部分所谓的「坑」都是由于自己的无知所造成的。首先来说说我遇到的问题吧,我的一个项目里需要在 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 帮我们再作一次转义就得到 \\\\\" 了。

    理顺一下:

    • 我们需要一个 \ 来给替换函数转义 \
    • 我们还需要在每个 \ 前再加一个 \ 来给 Java 编译器转义
    • 我们再还需要一个 \ 来给 Java 编译器转义 "

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

    相关文章

      网友评论

        本文标题:Java replaceAll 方法的「天坑」

        本文链接:https://www.haomeiwen.com/subject/vmatmttx.html