【问题标题】:How to escape text for regular expression in Java如何在Java中为正则表达式转义文本
【发布时间】:2010-09-08 18:40:24
【问题描述】:

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

【问题讨论】:

    标签: java regex escaping


    【解决方案1】:

    Java 1.5, yes:

    Pattern.quote("$5");
    

    【讨论】:

    • 请注意这不会转义字符串本身,而是使用\Q\E 包装它。这可能会导致意想不到的结果,例如Pattern.quote("*.wav").replaceAll("*",".*") 将导致\Q.*.wav\E 而不是.*\.wav,正如您所料。
    • 我只想指出,这种转义方式也适用于您之后引入的表达式。这可能令人惊讶。如果您执行"mouse".toUpperCase().replaceAll("OUS","ic"),它将返回MicE。您不会期望它返回 MICE,因为您没有在 ic 上应用 toUpperCase()。在我的示例中,quote() 也由 replaceAll() 应用于 .* 插入集。你必须做点别的事情,也许.replaceAll("*","\\E.*\\Q") 会起作用,但这是违反直觉的。
    • @Parameleon:对应问题的最佳解决方案是使用 split-map-mkString 方法。 ".wav".split("\\.").map(Pattern.quote).mkString(".").r
    • @Paramaleon 如果它确实通过添加单独的转义来工作,那么你的初始示例仍然不会做你想要的......如果它单独转义字符,它会将*.wav 变成正则表达式模式@ 987654338@,replaceAll 会将其转换为\.*\.wav,这意味着它将匹配名称由任意数量的句点后跟.wav 组成的文件。你很可能需要replaceAll("\\*", ".*"),如果他们使用更脆弱的实现,它依赖于识别所有可能的活动正则表达式字符并单独转义它们......会容易得多吗?
    • @Paramaeleon:用例是"*.wav".replaceAll(Pattern.quote("*"), ".*")
    【解决方案2】:

    我想你要的是\Q$5\E。另请参阅 Java5 中引入的Pattern.quote(s)

    详见Patternjavadoc。

    【讨论】:

    • 我很好奇这和使用 LITERAL 标志之间是否有任何区别,因为 javadoc 说没有嵌入式标志来打开和关闭 LITERAL:java.sun.com/j2se/1.5.0/docs/api/java/util/regex/…
    • 请注意,只有在您知道输入的情况下,才能真正使用 \Q 和 \E。 Pattern.quote(s) 还将处理您的文本实际包含这些序列的情况。
    【解决方案3】:

    Pattern.quoteMatcher.quoteReplacement 之间的区别在我看到以下示例之前我并不清楚

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

    【讨论】:

    • 具体来说,Pattern.quote 替换正则表达式搜索字符串中的特殊字符,如 .|+() 等,Matcher.quoteReplacement 替换替换字符串中的特殊字符,如反向引用的 \1。
    • 我不同意。 Pattern.quote 用 \Q 和 \E 包装它的参数。它不会转义特殊字符。
    • Matcher.quoteReplacement("4$&%$") 产生“4\$&%\$”。它转义了特殊字符。
    • 换句话说:quoteReplacement 只关心$\ 这两个符号,例如可以在替换字符串中用作反向引用$1\1。因此,它不能用于转义/引用正则表达式。
    • 太棒了。这是一个示例,我们希望将$Group$ 替换为T$UYO$HI$ 符号在模式和替换中都很特殊:"$Group$ Members".replaceFirst(Pattern.quote("$Group$"), Matcher.quoteReplacement("T$UYO$HI"))
    【解决方案4】:

    首先,如果

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

    它不会在末尾添加 1。它将查看第一个匹配组的搜索正则表达式并将其插入。这就是替换文本中 $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));
    

    解决了这个问题。用户可以毫无问题地输入任何类型的字符,包括美元符号。它的行为完全符合您的预期。

    【讨论】:

      【解决方案5】:

      要拥有受保护的模式,您可以用“\\\\”替换所有符号,数字和字母除外。之后,您可以在该受保护的模式中放入您的特殊符号,以使该模式不像愚蠢的引用文本那样工作,而是真正像一个模式,而是您自己的模式。没有用户特殊符号。

      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 ])”。
      • 小错别字,大后果:"([^a-zA-z0-9])" 也不匹配(即不能转义) [, \, ], ^ 你当然想拥有逃跑了!错字是第二个“z”,应该是“Z”,否则包括从 ASCII 65 到 ASCII 122 的所有内容
      【解决方案6】:

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

      Pattern.compile(textToFormat, Pattern.LITERAL);
      

      【讨论】:

      • 特别好用,可以和Pattern.CASE_INSENSITIVE结合使用
      【解决方案7】:

      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"));
      

      【讨论】:

      • +1 这非常适合将用户指定的非标准字符字符串转换为与正则表达式兼容的模式。我正在使用它来强制执行密码中的这些字符。谢谢。
      【解决方案8】:

      ^(Negation) 符号用于匹配不在字符组中的内容。

      这是Regular Expressions的链接

      这是关于否定的图像信息:

      【讨论】:

      • 我根本不明白这是如何解决这个问题的。
      猜你喜欢
      • 2011-07-04
      • 2013-01-18
      • 2017-12-15
      • 1970-01-01
      • 1970-01-01
      • 2012-02-25
      • 2022-01-16
      相关资源
      最近更新 更多