编译带有字符 class 和单词边界的冗长 Java 正则表达式时出错

Error compiling a verbose Java regex with character class and word boundary

为什么这个模式编译失败:

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 正则表达式编译器中的错误,还是我遗漏了什么?我喜欢在冗长的正则表达式中使用 [ ] 而不是反斜杠-反斜杠-space 因为它可以节省一些视觉噪音。但显然它们并不相同!

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

不知何故,冗长的正则表达式 (?x) 和包含单个 space [ ] 的字符 class 的组合会抛出编译器并使其无法识别词边界转义\b


使用 Java 最高 1.8 进行测试。0_151

I like to use [ ] in verbose regex instead of backslash-backslash-space because it saves some visual noise. But apparently they are not the same!

"[ ]""\ " 甚至 " ".

相同

问题是 (?x) 一开始启用 评论模式 。正如 documentation 所述

Permits whitespace and comments in pattern.
In this mode, whitespace is ignored, and embedded comments starting with # are ignored until the end of a line.
Comments mode can also be enabled via the embedded flag expression (?x).

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

改用" \b"。或者,通过使用反斜杠将其转义,将 space 保留在注释模式中:"(?x)[\ ]\b""(?x)\ \b".

解决方法

除了分别转义与 [ ] 完全相同的空格之外,您可以为整个正则表达式启用 x 模式,但在处理需要空格的模式时禁用它,内联:

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

或者替代方法是使用 qouting meta-characters \Q...\E:

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

为什么 Exception

在扩展模式或注释模式 (x) 中,空格会被忽略,但处理字符 class 中的空格在各种风格中的处理方式不同。

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

In this mode, whitespace is ignored...

期间。所以这个 [ ] 等于这个 [] 这是无效的并抛出一个 PatternSyntaxException 异常。

除了Java脚本之外几乎所有的正则表达式风格都需要一个字符class来拥有至少一个数据单元。他们将空字符 class 视为需要右括号的未闭合集。也就是说,[]] 在大多数口味中都有效。

[ ] 上不同风格的自由间距模式:

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

看起来因为 free-spacing (verbose) mode (?x) space in [ ] 被忽略了,所以正则表达式引擎将您的正则表达式视为 []\b.
如果我们删除 \b,它会看起来像 [],我们会得到关于 Unclosed character class 的错误 - 字符 class 不能为空,所以 ] 直接放在后面[ 被视为属于 class 的第一个字符,而不是结束字符 class.

的元符号

因此,由于 [ 未闭合,正则表达式引擎将 \b 视为 内部 字符 class。但是 \b 不能放在那里(它不代表字符而是 "place")所以我们看到关于 "unsupported escape sequence" 的错误(在字符 class 内,但那部分是跳过)。

换句话说,您不能使用 [ ] 在详细模式下转义 space(至少在 Java 中)。您需要使用 "\ ""[\ ]".

让我们分析一下到底发生了什么。

看看java.util.regex.Pattern

的源码

Permits whitespace and comments in pattern. In this mode, whitespace is ignored, and embedded comments starting with # are ignored until the end of a line.

Comments mode can also be enabled via the embedded flag expression (?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;
}

在你的情况下,你的正则表达式中有一个白色的 space (?x)[ ]\b 这将 return 一些东西(我无法正确分析它):

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

不等于 ch 这里抛出一个异常

throw error(s);

这是 Java 在 Pattern class 中的 peekPastWhitespace() 方法中的错误。追查整个问题...我决定看一下 OpenJDK 8-b132's Pattern implementation。让我们从头开始敲打它:

  1. compile() 在第 1696 行调用 expr()
  2. expr() 在第 1996 行调用 sequence()
  3. sequence() 在第 2063 行调用 clazz(),因为遇到 [ 的情况
  4. clazz() 在第 2509 行调用 peek()
  5. peek() 在第 1830 行调用 peekPastWhitespace(),因为 if(has(COMMENTS)) 的计算结果为 true(由于添加了 x 标志 (?x)模式的开头)
  6. peekPastWhitespace()(在下方发布)跳过模式中的 all spaces。

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 中的字符 class 不支持 \b。此外,一旦你解决了 \b 问题,你的角色 class 也没有结束 ].

解决此问题的方法:

  1. \ </code> 如 OP 所述,只需使用双反斜杠和 space</li> <li><code>[\ ] 转义字符 class 中的 space 以便按字面解释
  2. [ ](?x)\b 将内联修饰符放在字符 class
  3. 之后