如何在Java中转义文本以进行正则表达式


320

Java是否具有内置的转义任意文本方式,以便可以将其包含在正则表达式中?例如,如果我的用户输入“ $ 5”,我想在输入结束后完全匹配而不是“ 5”。

Answers:


450

Java 1.5开始,是的

Pattern.quote("$5");

88
请注意,这不会转义字符串本身,而是使用\Qand进行包装\E。这可能会导致意外的结果,例如,可能Pattern.quote("*.wav").replaceAll("*",".*")会导致\Q.*.wav\E.*\.wav,而不是您期望的那样。
Matthias Ronge

11
@Paramaeleon为什么会期望foo(x).bar()== x.bar()?
迈克尔

7
@Paramaeleon我认为您误解了用例。
vikingsteve,

18
我只是想指出,这种转义方式也将转义应用于您随后引入的表达式。这可能令人惊讶。如果这样做"mouse".toUpperCase().replaceAll("OUS","ic"),它将返回MicE。你我就不会希望它返回MICE,因为你没有申请toUpperCase()ic。在我的示例中,quote()也将其应用于插入.*replaceAll()。您必须做其他事情,也许.replaceAll("*","\\E.*\\Q")会行得通,但这是违反直觉的。
Matthias Ronge 2013年

2
@Paramaleon如果它通过添加单个转义符而起作用,那么您的初始示例仍然不会执行您想要的操作...如果单独地转义字符,它将*.wav变成regex模式\*\.wav,而replaceAll会将其变成\.*\.wav,这意味着匹配文件,其名称由任意数量的句点后跟组成.wavreplaceAll("\\*", ".*")如果它们采用了更加脆弱的实现方式,而这种实现方式依赖于识别所有可能的活动正则表达式字符并逐个转义,那么您最有可能需要……会那么容易吗?
Theodore Murdock

112

在我看到以下示例之前,Pattern.quote和之间的区别Matcher.quoteReplacement对我尚不清楚

s.replaceFirst(Pattern.quote("text to replace"), 
               Matcher.quoteReplacement("replacement text"));

29
具体来说,Pattern.quote替换正则表达式搜索字符串中的特殊字符,如。| +()等,并Matcher.quoteReplacement替换替换字符串中的特殊字符,如\ 1作为反向引用。
史蒂文

9
我不同意 Pattern.quote用\ Q和\ E包装其参数。它不会转义特殊字符。
David Medinets,2015年

5
Matcher.quoteReplacement(“ 4 $&%$”)产生“ 4 \ $&%\ $”。它转义特殊字符。
David Medinets,2015年

4
换句话说:quoteReplacement只关心两个符号$\ 例如可以在替换字符串中用作反向引用$1\1。因此,不得将其用于转义/引用正则表达式。
SebastianH

1
太棒了 这里我们要替换的例子$Group$T$UYO$HI。该$标志为特殊的无论是在模式和替换:"$Group$ Members".replaceFirst(Pattern.quote("$Group$"), Matcher.quoteReplacement("T$UYO$HI"))
阿伦

29

响应可能为时已晚,但是您也可以使用Pattern.LITERAL,它在格式化时会忽略所有特殊字符:

Pattern.compile(textToFormat, Pattern.LITERAL);

它特别好,因为您可以将其与Pattern.CASE_INSENSITIVE
mjjaniec

13

我想你所追求的是\Q$5\E。另请参见Pattern.quote(s)Java5中介绍的内容。

有关详细信息,请参见模式 javadoc。


我很好奇这和使用LITERAL标志之间是否有区别,因为javadoc说没有嵌入式标志来打开和关闭LITERAL:java.sun.com/j2se/1.5.0/docs/api/java/ util / regex /…
克里斯·马佐拉

15
请注意,仅在知道输入内容的情况下,使用\ Q和\ E确实可以。Pattern.quote(s)还将处理您的文本实际包含这些序列的情况。
Jeremy Huiskamp

10

首先,如果

  • 您使用replaceAll()
  • 您不要使用Matcher.quoteReplacement()
  • 要替换的文本包括$ 1

它不会以1结尾。它将查看第一个匹配组和子THAT in的搜索正则表达式。这就是$ 1,$ 2或$ 3在替换文本中的含义:匹配搜索模式中的组。

我经常将一长串文本插入.properties文件,然后从中生成电子邮件主题和正文。实际上,这似乎是在Spring Framework中执行i18n的默认方法。我将XML标记作为占位符放入字符串中,并使用replaceAll()在运行时用值替换XML标记。

我遇到一个问题,用户输入带有美元符号的美元和美分的数字。replaceAll()令人窒息,在stracktrace中显示以下内容:

java.lang.IndexOutOfBoundsException: No group 3
at java.util.regex.Matcher.start(Matcher.java:374)
at java.util.regex.Matcher.appendReplacement(Matcher.java:748)
at java.util.regex.Matcher.replaceAll(Matcher.java:823)
at java.lang.String.replaceAll(String.java:2201)

在这种情况下,用户在输入中的某处输入了“ $ 3”,replaceAll()继续在搜索正则表达式中查找第三个匹配组,但没有找到一个匹配组。

鉴于:

// "msg" is a string from a .properties file, containing "<userInput />" among other tags
// "userInput" is a String containing the user's input

更换

msg = msg.replaceAll("<userInput \\/>", userInput);

msg = msg.replaceAll("<userInput \\/>", Matcher.quoteReplacement(userInput));

解决了问题。用户可以输入任何种类的字符,包括美元符号,而不会出现问题。它的行为完全符合您的期望。


6

要获得受保护的模式,可以将所有符号替换为“ \\\\”,数字和字母除外。然后,您可以在该受保护的模式中添加特殊符号,以使该模式不像愚蠢的引用文本那样工作,而实际上像木套纸,而是您自己的样式。没有用户特殊符号。

public class Test {
    public static void main(String[] args) {
        String str = "y z (111)";
        String p1 = "x x (111)";
        String p2 = ".* .* \\(111\\)";

        p1 = escapeRE(p1);

        p1 = p1.replace("x", ".*");

        System.out.println( p1 + "-->" + str.matches(p1) ); 
            //.*\ .*\ \(111\)-->true
        System.out.println( p2 + "-->" + str.matches(p2) ); 
            //.* .* \(111\)-->true
    }

    public static String escapeRE(String str) {
        //Pattern escaper = Pattern.compile("([^a-zA-z0-9])");
        //return escaper.matcher(str).replaceAll("\\\\$1");
        return str.replaceAll("([^a-zA-Z0-9])", "\\\\$1");
    }
}

您不必逃脱空间。因此,您可以将样式更改为“([[^ a-zA-z0-9])””。
Erel Segal-Halevi 2013年

5
小错字,大后果:“([^ a-zA-z0-9])”也与您肯定想逃脱的[,\,],^不匹配(即不逃避!)!错字是第二个“ z”,应该是“ Z”,否则包括从ASCII 65到ASCII 122的所有内容
Zefiro

3

Pattern.quote(“ blabla”)效果很好。

Pattern.quote()可以很好地工作。它用字符“ \ Q ”和“ \ E ” 括起句子,如果确实转义了“ \ Q”和“ \ E”。但是,如果您需要执行真正的正则表达式转义(或自定义转义),则可以使用以下代码:

String someText = "Some/s/wText*/,**";
System.out.println(someText.replaceAll("[-\\[\\]{}()*+?.,\\\\\\\\^$|#\\\\s]", "\\\\$0"));

此方法返回:Some / \ s / wText * / \,**

代码示例和测试:

String someText = "Some\\E/s/wText*/,**";
System.out.println("Pattern.quote: "+ Pattern.quote(someText));
System.out.println("Full escape: "+someText.replaceAll("[-\\[\\]{}()*+?.,\\\\\\\\^$|#\\\\s]", "\\\\$0"));

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.