编译具有字符类和字边界的详细Java正则表达式时出错

Tob*_*bia 45 java regex verbose

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

Pattern.compile("(?x)[ ]\\b");
Run Code Online (Sandbox Code Playgroud)

错误

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

虽然以下相同的工作?

Pattern.compile("(?x)\\ \\b");
Pattern.compile("[ ]\\b");
Pattern.compile(" \\b");
Run Code Online (Sandbox Code Playgroud)

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

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

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


使用Java测试高达1.8.0_151

Soc*_*owi 31

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

"[ ]""\\ "和甚至是一样的" ".

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

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

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

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

  • @SergiyKolesnikov因为反斜杠会逃离空间并阻止它被移除. (7认同)

ctw*_*els 22

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

  1. compile()expr()在1696线上打电话
  2. expr()sequence()1996年在线通话
  3. sequence()电话clazz()线2063以来的情况下[遭到了
  4. clazz()拨打peek()2509号线
  5. peek()peekPastWhitespace()因为if(has(COMMENTS))评估为true(因为在模式的开头添加了x标志)(?x),所以在第1830行调用
  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;
}
Run Code Online (Sandbox Code Playgroud)

parsePastWhitespace()方法中存在相同的错误.

您的正则表达式被解释为[]\\b,这是您的错误的原因,因为\bJava中的字符类不支持.此外,一旦解决了\b问题,您的角色类也没有结束].

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

  1. \\ As the OP mentioned, simply use double backslash and space
  2. [\\ ] 转义字符类中的空格,以便按字面解释
  3. [ ](?x)\\b 将内联修饰符放在字符类之后

  • OP在这里.多年来,我一直在用Perl,Python,PHP,libpcre和其他"PCRE"风格编写扩展/详细正则表达式.这是我第一次看到在字符类中跳过空格.如果Java的正则表达式与Perl和PCRE兼容,那么是的,这是代码中的一个错误.否则它是文档中的错误,因为它没有指出这与事实上的标准的偏差. (3认同)
  • 即使在(?x)模式下,Perl也将[]解释为文字空间(这在`perlre(1p)`中特别提到:"括号中的字符类不受/ x"影响),而Perl _invented_(?x)模式,所以我认为这应该是决定性的:这是一个错误. (2认同)

Psh*_*emo 12

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

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

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


rev*_*evo 5

解决方法

除了单独转义的空格之外[ ],你可以x为整个正则表达式启用模式,但在处理需要空格的模式时禁用它,内联:

(?x)match-this-(?-x: with spaces )\\b
    ^^^^^^^^^^^     ^^^^^^^^^^^^^ ^^^
    `x` is on            off       on
Run Code Online (Sandbox Code Playgroud)

或另一种方法是使用qouting元字符\Q...\E:

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

为什么Exception

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

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

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

期.所以这[ ]等于这个[]无效并引发PatternSyntaxException异常.

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

不同口味的自由间隔模式[ ]:

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


YCF*_*F_L 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

如果你注意到你的代码调用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;
}
Run Code Online (Sandbox Code Playgroud)

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

    if (ch != testChar) {
        throw error(s);
    }
Run Code Online (Sandbox Code Playgroud)

这不等于ch,这里抛出异常

throw error(s);
Run Code Online (Sandbox Code Playgroud)