编译具有字符类和单词边界

时间:2018-03-13 19:05:00

标签: java regex verbose

为什么这种模式无法编译:

Pattern.compile("(?x)[ ]\\b");

错误

ERROR java.util.regex.PatternSyntaxException:
Illegal/unsupported escape sequence near index 8
(?x)[ ]\b
        ^
at java_util_regex_Pattern$compile.call (Unknown Source)

虽然以下相同的工作有效吗?

Pattern.compile("(?x)\\ \\b");
Pattern.compile("[ ]\\b");
Pattern.compile(" \\b");

这是Java正则表达式编译器中的错误,还是我遗漏了什么?我喜欢在详细的正则表达式中使用[ ]而不是反斜杠 - 反斜杠空间,因为它可以节省一些视觉噪音。但显然他们不一样!

PS:这个问题与反斜杠无关。它是关于使用包含单个空格[ ]而不是使用反斜杠的字符类在冗长的正则表达式中转义空格。

以某种方式,详细的正则表达式(?x)和包含单个空格[ ]的字符类的组合会抛弃编译器并使其无法识别单词边界转义\b

使用Java测试高达1.8.0_151

5 个答案:

答案 0 :(得分:31)

  

我喜欢在详细的正则表达式中使用[ ]而不是反斜杠 - 反斜杠空间,因为它可以节省一些视觉噪音。但显然他们不一样!

"[ ]""\\ "" "相同。

问题是(?x)在开始时启用评论模式。作为documentation

  

允许使用模式中的空格和注释   在此模式下,将忽略空格,并开始嵌入注释   #被忽略,直到一行结束   注释模式也可以通过嵌入的标志表达式启用   (?x)

在注释模式下,正则表达式"(?x)[ ]\\b""[]\\b"相同并且不会编译,因为空字符类[]未被解析为空,但解析为{{1} }(包含文字"[\\]")的未闭合字符类。

请改用]。或者,通过使用反斜杠:" \\b""(?x)[\\ ]\\b"转义空格,在评论模式中保留空格。

答案 1 :(得分:22)

这是peekPastWhitespace()类中Java Pattern方法中的错误。追查整个问题...我决定看看OpenJDK 8-b132's Pattern implementation。让我们从顶部开始锤击这个:

  1. compile()在第1696行调用expr()
  2. expr()在1996年致电sequence()
  3. 自从sequence()的案例得到满足后,
  4. clazz()在第2063行调用[
  5. clazz()在第2509行调用peek()
  6. peek()在第1830行调用peekPastWhitespace(),因为if(has(COMMENTS))评估为true(由于在开头添加了x标记(?x)模式)
  7. peekPastWhitespace()(在下面发布)跳过模式中的所有空格。
  8. peekPastWhitespace()

    private int peekPastWhitespace(int ch) {
        while (ASCII.isSpace(ch) || ch == '#') {
            while (ASCII.isSpace(ch))
                ch = temp[++cursor]
            if (ch == '#') {
                ch = peekPastLine();
            }
        }
        return ch;
    }
    

    parsePastWhitespace()方法中存在同样的错误。

    您的正则表达式被解释为[]\\b,这是导致错误的原因,因为Java中的字符类不支持\b。此外,修复\b问题后,您的角色类也没有结束]

    您可以采取哪些措施来解决此问题:

    1. \\ 正如OP所提到的,只需使用双反斜杠和空格
    2. [\\ ]转义字符类中的空格,以便按字面解释
    3. [ ](?x)\\b将内联修饰符放在字符类
    4. 之后

答案 2 :(得分:12)

看起来因为[ ]中的free-spacing (verbose) mode (?x)空格被忽略,因此正则表达式引擎会将您的正则表达式视为[]\\b
如果我们删除\\b,则会看到[],我们会收到有关Unclosed character class的错误 - 字符类不能为空,因此]会直接放在[之后被视为属于该类的第一个字符,而不是关闭字符类的元符号。

因为[未公开,所以正则表达式引擎会将\b视为位于该字符类中。但\b不能放在那里(它不代表字符而是“地方”)所以我们看到有关“不支持的转义序列”的错误(在字符类内部,但跳过了该部分)。

换句话说,您无法使用[ ]以详细模式(至少在Java中)转义空间。您需要使用"\\ ""[\\ ]"

答案 3 :(得分:5)

解决方法

除了单独转义与[ ]字面上相同的空格外,您可以为整个正则表达式启用x模式,但在处理需要空格的图案时禁用它,内联:

(?x)match-this-(?-x: with spaces )\\b
    ^^^^^^^^^^^     ^^^^^^^^^^^^^ ^^^
    `x` is on            off       on

或替代方案是使用qouting元字符\Q...\E

(?x)match-this-\Q with s p a c e s \E\\b
    ^^^^^^^^^^^  ^^^^^^^^^^^^^^^^^^  ^^^
    `x` is on            off          on

为什么是Exception

在扩展或评论模式(x)中,忽略空格,但处理各种风格的字符类中的空格的方式不同。

例如在PCRE中,除了字符类中的字符外,所有空白字符都被忽略。这意味着[ ]是一个有效的正则表达式,但Java没有例外:

  

在此模式下,空格被忽略......

周期。因此,此[ ]等于此[],它无效并引发PatternSyntaxException例外。

除了JavaScript之外,几乎所有的正则表达式都需要一个字符类才能拥有至少一个数据单元。它们将空字符类视为需要闭括号的未闭合集。这么说,[]]在大多数风格中都有效。

[ ]上不同味道的自由间距模式:

  • PCRE有效
  • .NET有效
  • Perl有效
  • Ruby有效
  • TCL有效
  • Java 7无效
  • Java 8无效

答案 4 :(得分:5)

让我们分析究竟发生了什么。

查看java.util.regex.Pattern

的源代码
  

允许使用模式中的空格和注释。 在此模式下,空格   被忽略,并且忽略以#开头的嵌入式注释   一行的结尾。

     

也可以通过嵌入的标志表达式启用注释模式   (ΔX)。

您的正则表达式引导您完成此line

private void accept(int ch, String s) {
    int testChar = temp[cursor++];
    if (has(COMMENTS))
        testChar = parsePastWhitespace(testChar);
    if (ch != testChar) {
        throw error(s);
    }
}

如果您发现代码调用parsePastWhitespace(testChar);

private int parsePastWhitespace(int ch) {
    while (ASCII.isSpace(ch) || ch == '#') {
        while (ASCII.isSpace(ch))//<----------------Here is the key of your error
            ch = temp[cursor++];
        if (ch == '#')
            ch = parsePastLine();
    }
    return ch;
}

在您的情况下,正则表达式(?x)[ ]\\b中有空格会返回一些内容(我无法正确分析):

    if (ch != testChar) {
        throw error(s);
    }

不等于ch,此处抛出异常

throw error(s);