字符类交集运算符&&
,根据其功能的定义,应该是可交换的。[a&&b]
应该匹配与任何 a 和 b 完全相同的字符[b&&a]
。我发现以下模式都满足这个标准。
[a-z&&abcd]
如同[abcd&&a-z]
[a-z&&ab[cd]]
如同[ab[cd]&&a-z]
[a-z&&[ab][cd]]
如同[[ab][cd]&&a-z]
它们都等价于[abcd]
。但是,如果表达[a-z&&[ab]cd]
,这不再是真的。该表达式仅匹配c
and d
,但不匹配a
and b
。但是,翻转版本[[ab]cd&&a-z]
与其他模式一样匹配所有四个字符。换句话说
[[ab]cd&&a-z]
不一样[a-z&&[ab]cd]
我进入的来源Pattern
找出了为什么会这样,我发现这是实现交集的方式(Java 1.8.0_60 JDK)
case '&':
// ...
ch = next();
if (ch == '&') {
ch = next();
CharProperty rightNode = null;
while (ch != ']' && ch != '&') {
if (ch == '[') {
if (rightNode == null)
rightNode = clazz(true);
else
rightNode = union(rightNode, clazz(true));
} else { // abc&&def
unread();
rightNode = clazz(false); // here is what happens
}
ch = peek();
}
请注意,标记的行是
rightNode = clazz(false);
并不是
rightNode = union(rightNode, clazz(true));
换句话说,在 的右侧&&
,每当遇到不在嵌套字符类中的第一个字符时,模式解析器都会假定它之前没有任何内容。因此,在 之后&&
,解析器读[ab]
入rightNode
,然后读取cd
,但不是与 合并[ab]
,而是覆盖它。
我知道实际上没有人写像 regex 这样的正则表达式[a-z&&[ab]cd]
,但是文档仍然暗示它应该可以工作。这是实现中的错误,还是实际上应该以这种方式工作?