【问题标题】:java 8 regular expression for meta characters [duplicate]元字符的java 8正则表达式[重复]
【发布时间】:2020-02-03 06:58:59
【问题描述】:

尝试编写一个正则表达式来检查句子是否为元字符“我需要为购买支付 50 美元,我应该使用 CASH|CC”。在这句话中,我需要确定是否存在元字符。 \\\\$^(\\\\$)\\$Pattern.matches("^([\\\\$]$)", text); 识别特殊字符的正确语法是什么。我不需要替换,只需确定句子是否包含这些字符。

【问题讨论】:

  • @user85421,不能使用String.contains,需要使用元字符集进行过滤。 String text = "Cash$50"; System.out.println(text.matches("\\\\$")); boolean match = Pattern.matches("\\\\$", text); System.out.println(match); 两者都打印错误
  • 过滤器?但是您只要求识别(查找)?如果你想find(),不要使用matches()(或者它必须是".*\\$.*"才能匹配任意数量的字符,一美元和任意数量的字符)并且只需要2个反斜杠
  • 示例:Pattern.compile("\\$").matcher("Cash $50").find()Pattern.matches(".*\\$.*", "Cash $50")

标签: java regex java-8


【解决方案1】:

如果你想知道一个字符串是否包含元字符,你可以这样使用:

boolean hasIt = sentence.chars().anyMatch(c -> "\\.[]{}()*+?^$|".indexOf(c) >= 0);

不使用Regex引擎,你不需要引用对它有特殊含义的字符。

使用Pattern.matches 会给任务带来三个不必要的障碍。首先,您必须正确引用所有字符,然后,您需要一个正则表达式构造来将字符转换为替代字符,例如[abc]a|b|c,第三,matches 检查整​​个字符串是否匹配模式,而不是包含一个出现,所以你需要像 .*pattern.* 这样的东西来制作 matches表现得像find,如果你坚持的话。

这导致了这个任务的xy-problem。目前尚不清楚您实际上想要检查哪些元字符以及为什么首先需要这些信息。

如果您想在另一个文本中搜索这句话,只需使用Pattern.compile(sentence, Pattern.LITERAL) 禁用元字符的解释。或者Pattern.quote(sentence),当你想组装一个包含句子的模式时。

但如果你不想搜索它,这些信息就没有相关性。请注意,“这是一个元字符吗?”可能会导致与“是否需要引用?”不同的答案。甚至this tutorial 也以一种误导的方式结合了这些问题。在两个相近的地方,它命名元字符并描述引用语法,从而导致所有这些字符都需要引用的错误印象。

例如,- 仅在字符类中具有特殊含义,因此如果没有字符类,您可以通过 [ 的存在检测到,- 并不意味着元字符的存在。但是,虽然 - 确实需要在字符类中引用,但字符 =! 只是在特定上下文中的元字符,这需要元字符,因此它们从不需要引用。

但是,如果您尝试检查元字符来决定是使用正则表达式引擎还是执行纯文本搜索,例如通过String.indexOf,您正在执行过早优化。这不仅是对开发工作的浪费,甚至在您拥有可以测量的实际代码之前进行优化通常会导致相反的结果。使用不包含元字符的字符串使用正则表达式引擎执行模式匹配可以导致比String 上的普通indexOf 更有效的搜索。在参考实现中,Regex 引擎使用Boyer Moore algorithm,而String 上的明文搜索方法使用朴素搜索。

【讨论】:

    【解决方案2】:

    编辑:正如评论者 Andreas 和 Holger 所提到的,正则表达式使用的元字符有时取决于语法子定义,例如字符类、特定序列(前瞻、后视、...),因此本质上不是每个元字符瑟。有些只是特定上下文中的元字符。但是,此处提供的答案将包括所有可能的元字符,但只有在以 \ 为前缀时才成为元字符的运算符除外。但是,这意味着有时字符会在它们实际上不是元字符的位置匹配。

    这个问题有一半的答案:List of all special characters that need to be escaped in a regex

    可以看Pattern类的javadoc:http://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html

    Java 正则表达式系统没有为它自己的特殊字符公开任何字符类(很遗憾)。

    特殊构造(命名捕获和非捕获)
    (?X) X,作为命名捕获组
    (?:X) X,作为非捕获组
    (?idmsuxU-idmsuxU) 无,但将匹配标志 i d m s u x U 打开 - 关闭
    (?idmsux-idmsux:X) X,作为具有给定标志的非捕获组 i d m s u x on - off
    (?=X) X,通过零宽度正向前瞻
    ( ?!X) X,通过零宽度负前瞻

    仅此块包含很多(尽管不是全部)元字符。引文的最后两行我不能省略,因为字符序列混淆了这个页面的解析器。 我建议如下:

    public static final Pattern META_CHARS = Pattern.compile("[\\\\\\]\\[(){}\\-!$?*+<>\\:\\.\\=\\,\\|^]");
    

    但请注意,此列表很可能不完整,并且其中包含典型字符,例如 ,.,它们是正则表达式语法的一部分。所以你可能有很多逃避的事情要做......

    从那里你可以:

    Matcher metaDetector = META_CHARS.matcher(stringToTest);
    if (metaDetector.find()) {
        // this is the found meta character...
        String metaCharacter = metaDetector.group(0);
        System.out.print(metaCharacter);
    }
    

    如果你想找到所有元字符,那么在上面的代码sn-p中用if创建一个while。如果这样做,对于"I need to make \\payment{[ of $50 for !!the purc\"hase, sh###ould i use CASH|CC." 行,您会收到\{[$!!,|.,这是正确的,因为#" 不是正则表达式中的元字符。

    正如 Andreas 正确提到的,确切的模式可以简化为 "[\\\\\\]\\[(){}^$?*+.|]",因为这会告诉你,是否至少存在一个元字符。但是,如果存在多个元字符,这可能会丢失一些元字符。如果这不重要,那么较短的链就足够了。

    【讨论】:

    • "[\\\\\\]\\[(){}\\-!$?*+&lt;&gt;\\:\\.\\=\\,\\|]" 有太多字符,以及不必要的转义。 --- 1) 这些不需要在字符类中转义::.=,| --- 2) 这些只是紧跟在 (? 特殊捕获组前缀之后的元字符::=!&lt;&gt; --- 3)这只是字符类中的元字符:- --- 4) 这只是 {} 量词中的元字符:, --- 5) 缺失:^ --- 结论:应该是:"[\\\\\\]\\[(){}^$?*+.|]"
    • @Andreas 你是对的。但是如果你一直这样下去,如果存在两个反斜杠(即单词,如果元字符已经转义)怎么办?我只打印了一个解决方案来查找可能的元字符。不能保证这些实际上是未转义的元字符。
    • 带有 any\ 反斜杠的纯文本是如果用作正则表达式时需要转义的文本,这是您甚至会关心正则表达式的唯一时间元字符。除非您首先将 \ 视为元字符,否则元字符不能“已经转义”,如果您这样做,则文本包含元字符,这就是我们正在寻找的。简而言之,如果它们在文本中加倍,则没有区别。
    • 字符 = 仅在前面有 (?(?&lt; 时才具有特殊含义,这使它成为一个元字符,例如A,因为A在前面有`\`时有特殊含义。这使得字符作为“元字符”的分类变得模糊,这可能是正则表达式引擎不提供“为它自己的特殊字符提供字符类”的原因;不清楚哪些字符应该属于这个类。
    • 问题是,如果不知道为什么 OP 实际需要此检查,就不可能选择正确的字符。正如我在回答中试图解释的那样,“这是一个元字符吗?”与“它需要引用吗?”是一个不同的问题。这甚至是一个不同于“我引用这句话会造成伤害吗?”……
    猜你喜欢
    • 1970-01-01
    • 2011-12-15
    • 1970-01-01
    • 2020-09-16
    • 2015-09-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多