编译带有字符 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 中)。您需要使用 "\ "
或 "[\ ]"
.
让我们分析一下到底发生了什么。
的源码
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。让我们从头开始敲打它:
compile()
在第 1696 行调用 expr()
expr()
在第 1996 行调用 sequence()
sequence()
在第 2063 行调用 clazz()
,因为遇到 [
的情况
clazz()
在第 2509 行调用 peek()
peek()
在第 1830 行调用 peekPastWhitespace()
,因为 if(has(COMMENTS))
的计算结果为 true
(由于添加了 x
标志 (?x)
模式的开头)
peekPastWhitespace()
(在下方发布)跳过模式中的 all spaces。
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 也没有结束 ]
.
解决此问题的方法:
\
</code> 如 OP 所述,只需使用双反斜杠和 space</li>
<li><code>[\ ]
转义字符 class 中的 space 以便按字面解释
[ ](?x)\b
将内联修饰符放在字符 class 之后
为什么这个模式编译失败:
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 中)。您需要使用 "\ "
或 "[\ ]"
.
让我们分析一下到底发生了什么。
的源码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。让我们从头开始敲打它:
compile()
在第 1696 行调用expr()
expr()
在第 1996 行调用sequence()
sequence()
在第 2063 行调用clazz()
,因为遇到[
的情况clazz()
在第 2509 行调用peek()
peek()
在第 1830 行调用peekPastWhitespace()
,因为if(has(COMMENTS))
的计算结果为true
(由于添加了x
标志(?x)
模式的开头)peekPastWhitespace()
(在下方发布)跳过模式中的 all spaces。
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 也没有结束 ]
.
解决此问题的方法:
\
</code> 如 OP 所述,只需使用双反斜杠和 space</li> <li><code>[\ ]
转义字符 class 中的 space 以便按字面解释[ ](?x)\b
将内联修饰符放在字符 class 之后