我们可以在 lookbehind 表达式中使用量词吗?

Can we use quantifiers inside a lookbehind expression?

此问题特定于 Java 7/8。

使用量词的相当复杂的正则表达式在后向断言中被禁止使用,例如:

(?<=(a|b*)*)bc

因为它会导致运行时异常并显示如下消息:

look-behind group does not have obvious maximum length error

我猜这是因为 *+ 等量词是 "generally" 不允许的。

但是,以下内容确实有效:

(?<=a*)bc

为什么会这样?

SO 上有关于此问题的类似帖子:

  1. 对于其他语言: ruby, PCRE等-

  2. 有些帖子是针对 Java 的,但似乎没有提供具体的答案或参考资料,例如 this one. Mostly the answers state these quantifiers simply cannot be used. Also, regular-expressions 网站上的说法相同。

  3. This post 声明 Java 在实施后的外观中存在错误。

但是,我上面显示的示例在回顾中使用零个或多个量词 * 对于 Java 7/8 有效。

任何参考资料或解释都会有所帮助。

查看 Pattern 代码并尝试对其进行跟踪后,我确信这只是一个错误。这两个示例都应导致异常。但是测试这个的逻辑是不正确的。

这段代码出现在几个地方:

 temp = info.maxLength * cmax + maxL;
 info.maxLength = temp;
 if (temp < maxL) {
     info.maxValid = false;
 }

请注意,只要 maxLengthcmax 是非负数,除非发生溢出,否则 temp 永远不会小于 maxLmaxValid 最终被lookbehind代码使用;如果 maxValidfalse,则抛出 "look-behind group does not have obvious maximum length error"

据我所知,在上面的

 {m,n} 这样的正则表达式中代码中,info.maxLength是"expression"的最大长度,cmax是量词的上界,maxL是"prefix"的最大长度。当量词为 *+ 时,上限设置为 Integer.MAX_VALUE。 (这里所有的变量都是int。)这意味着会溢出除非info.maxLength为1,maxL为0

就是这种情况
(?<=a*)bc

因为带有量词的模式的长度为 1,并且在 a* 之前没有任何内容,这意味着 maxL 将为 0。这就是为什么这种情况会被忽略。

对于任何其他值,计算都会溢出,但这并不一定意味着 temp < maxL 为真。如果info.maxLength为偶数,则抛出异常;但是如果 info.maxLength 是奇数,如果 maxL 足够小,模式将编译。这是因为在数学上环绕的工作方式;试图检查溢出的代码非常错误。这意味着

(?<=a*)bc         // succeeds
(?<=(ab)*)bc      // throws exception
(?<=(abc)*)bc     // succeeds
(?<=(abcd)*)bc    // throws exception
(?<=(abcde)*)bc   // succeeds

还有:

(?<=xa*)bc        // throws exception
(?<=x(abc)*)bc    // succeeds

注意:需要注意的是,在你的例子正则表达式中,lookbehind是没有用的:

(?<=a*)bc

lookbehind 表示要测试当前位置前面是否有零次或多次出现的字母 a。这总是真实的,微不足道的。因此,lookbehind 在这里没有任何意义。同样,

(?<=a+)bc

等同于

(?<=a)bc

因为只要在当前位置之前有一个 a,那么还有多少都没有关系。所有不抛出异常的例子都一样,除了这个:

(?<=x(abc)*)bc    // succeeds

因为这里匹配器确实必须向后查找字符串中的 abc 并确保最后一个前面有 x。在这种情况下,Pattern 似乎应该抛出异常,但由于错误的溢出检查逻辑,事实并非如此。因此,我不确定它实际上是否会 return 正确的结果,或者它是否会崩溃或进入无限循环。不过,我没试过。

真的,代码应该直接检查 cmax 是否等于 Integer.MAX_VALUE,而不是在计算中使用它并希望代码稍后可以告诉结果是假的。