【问题标题】:Can we use quantifiers inside a lookbehind expression?我们可以在后向表达式中使用量词吗?
【发布时间】:2016-05-05 05:38:20
【问题描述】:

这个问题是 Java 7/8 特有的。

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

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

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

look-behind group does not have obvious maximum length error

我猜这是因为 *+ 这样的量词“通常”是不允许的。

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

(?<=a*)bc

为什么会这样?

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

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

  2. 有些帖子专门针对 Java,但似乎没有提供具体的答案或参考,例如 this one。大多数答案表明这些量词根本无法使用。此外,regular-expressions 网站声明相同。

  3. This post 表示 Java 在其实现的背后存在错误。

但是,我上面展示的在后视中使用零个或多个量词 * 的示例适用于 Java 7/8。

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

【问题讨论】:

  • (注意:这不是由 JLS 指定,而是由库文档指定。)
  • library documentation 也没有真正指定它。它向您介绍了描述特定行为的 O'Reilly 书籍。
  • 谢谢。我已经更正了。

标签: java regex-lookarounds


【解决方案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"

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

(?<=a*)bc

因为带有量词的模式的长度为 1,而在 a* 之前没有任何内容,这意味着 maxL 将是 0。这就是为什么这种情况下落空的原因。

对于任何其他值,计算都会溢出,但这并不一定意味着temp &lt; 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。这总是正确的,微不足道的。因此,后视在这里毫无用处。同样,

(?<=a+)bc

等价于

(?<=a)bc

只要在当前位置之前有一个a,可能还有多少并不重要。所有不抛出异常的示例都是如此,除了这个:

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

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

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

【讨论】:

    猜你喜欢
    • 2020-01-02
    • 2015-03-08
    • 1970-01-01
    • 2012-01-24
    • 2021-12-14
    • 1970-01-01
    • 2010-11-10
    • 2010-09-29
    • 1970-01-01
    相关资源
    最近更新 更多