45

为什么此模式无法编译:

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

4

5 回答 5

31

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

"[ ]"相同"\\ "或什至" "

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

允许模式中的空格和注释。
在这种模式下,空白将被忽略,并且以 开头的嵌入注释#将被忽略,直到行尾。
评论模式也可以通过嵌入的标志表达式启用 (?x)

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

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

于 2018-03-13T19:44:48.470 回答
22

这是类中JavapeekPastWhitespace()方法中的一个错误Pattern。追踪整个问题……我决定看一下OpenJDK 8-b132 的Pattern实现。让我们从顶部开始敲击它:

  1. compile()拨打expr()1696 号线
  2. expr()sequence()1996年在线电话
  3. sequence()clazz()自从[遇到了2063 行调用
  4. clazz()拨打peek()2509 号线
  5. peek()peekPastWhitespace()在第 1830 行调用,因为if(has(COMMENTS))计算结果为true(由于在模式的开头添加了x标志)(?x)
  6. peekPastWhitespace()(发布在下面)跳过模式中的所有空格。

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,这是导致错误的原因,因为\bJava 中的字符类不支持。此外,一旦您解决了\b问题,您的角色类也没有关闭].

你可以做些什么来解决这个问题:

  1. \\ 正如OP所提到的,只需使用双反斜杠和空格
  2. [\\ ]转义字符类中的空格,以便按字面意思解释
  3. [ ](?x)\\b将 inline 修饰符放在字符类之后
于 2018-03-13T19:59:18.527 回答
12

看起来因为自由间距(详细)模式(?x)空间[ ]被忽略,所以正则表达式引擎将您的正则表达式视为[]\\b.
如果我们删除\\b[],我们会看到这样的错误Unclosed character class- 字符类不能为空,因此]直接放在之后[被视为属于该类的第一个字符,而不是关闭字符类的元符号。

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

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

于 2018-03-13T19:46:26.993 回答
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 没有异常:

在这种模式下,空白被忽略...

时期。所以 this[ ]等于 this[]无效并抛出PatternSyntaxException异常。

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

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

  • PCRE有效的
  • .NET有效的
  • Perl有效的
  • Ruby有效的
  • TCL 有效的
  • Java 7无效的
  • Java 8无效的
于 2018-03-13T19:45:54.437 回答
5

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

看一下java.util.regex.Pattern的源码

允许模式中的空格和注释。在这种模式下,空格被忽略,并且以 # 开头的嵌入注释被忽略,直到行尾。

评论模式也可以通过嵌入的标志表达式 (?x) 启用。

您的正则表达式将您引导至此

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);
于 2018-03-13T19:48:31.443 回答