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

时间:2016-05-05 05:38:20

标签: java regex-lookarounds

此问题特定于Java 7/8。

使用量词的相当复杂的正则表达式在这样的后观断言中是被禁止的:

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

因为它会导致运行时异常,并带有如下消息:

look-behind group does not have obvious maximum length error

我猜这是因为*+等量词通常是&#34;&#34;不允许。

但是,以下方法确实有效:

(?<=a*)bc

为什么会这样?

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

  1. 对于其他语言: rubyPCRE等 -

  2. 有些帖子是针对Java的,但似乎没有提供具体的答案或引用,例如this。大多数答案都说明这些量词根本无法使用。另外,regular-expressions网站也说明了相同的内容。

  3. This post表示Java在实施方面存在缺陷。

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

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

1 个答案:

答案 0 :(得分:1)

在查看Pattern代码并尝试跟踪它之后,我确信这只是一个错误。这两个例子都应该导致异常。但是测试这个的逻辑是不正确的。

此代码出现在几个地方:

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

请注意,只要maxLengthcmax为非负,temp就不应小于maxL,除非发生溢出。最后一个代码使用maxValid;如果maxValidfalse,则会引发"look-behind group does not have obvious maximum length error"

据我所知,在&lt; prefix&gt; &lt; expression&gt; {m,n}这样的正则表达式中,在上面的代码中{{1} }是&#34;表达式&#34;的最大长度,info.maxLength是量词的上限,cmax是&#34;前缀&#34;的最大长度。当量词为maxL*时,上限设置为+。 (此处的所有变量均为Integer.MAX_VALUE。)这意味着除非int为1且info.maxLength为0 ,否则会出现溢出

就是这种情况
maxL

因为带有量词的模式的长度为1,并且(?<=a*)bc 之前没有任何内容,这意味着a*将为0.这就是为什么这个案例会破裂的原因。

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

maxL

此外:

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

注意:应该注意的是,在你的示例正则表达式中,lookbehind是无用的:

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

lookbehind表示测试当前位置是否在字母(?<=a*)bc 出现零次或多次之前。这总是正确的,琐碎的。因此,后视在这里没有任何意义。类似地,

a

相当于

(?<=a+)bc

因为只要在当前位置之前有一个(?<=a)bc ,所以可能会有多少并不重要。所有不会抛出异常的例子都是如此,除了这个:

a

因为匹配器必须向后搜索字符串中的(?<=x(abc)*)bc // succeeds 并确保最后一个字符串前面有abc。在这种情况下x似乎应该抛出一个异常,但是由于错误的溢出检查逻辑,它不是。因此,我不确定它是否会实际返回正确的结果,或者它是否会崩溃或进入无限循环。但是,我还没试过。

实际上,代码应该直接检查Pattern是否等于cmax,而不是在计算中使用它,并希望代码稍后可以告诉结果是伪造的。