代码之家  ›  专栏  ›  技术社区  ›  David Eyk

lookahead断言似乎对正则表达式中的替代项进行了短路排序。

  •  2
  • David Eyk  · 技术社区  · 16 年前

    我正在使用(python-flavered)正则表达式来识别经文引用的常见和特殊形式和缩写。给定以下详细代码段:

    >>> cp = re.compile(ur"""
        (?:(
            # Numbered books
            (?:(?:Third|Thir|Thi|III|3rd|Th|3)\ ? 
               (?:John|Joh|Jhn|Jo|Jn|Jn|J))
            # Other books
            |Thessalonians|John|Th|Jn)\ ? 
          # Lookahead for numbers or punctuation
          (?=[\d:., ]))
    
        |
    
        # Do the same check, this time at the end of the string.
        (
          (?:(?:Third|Thir|Thi|III|3rd|Th|3)\ ?
             (?:John|Joh|Jhn|Jo|Jn|Jn|J))
          |Thessalonians|John|Th|Jn)\.?$
        """, re.IGNORECASE | re.VERBOSE)
    
    >>> cp.match("Third John").group()
    'Third John'
    
    >>> cp.match("Th Jn").group()
    'Th'
    
    >>> cp.match("Th Jn ").group()
    'Th Jn'
    

    这段代码的目的是匹配各种形式的“第三个约翰”,以及形式的“帖撒罗尼迦人”和“约翰”自己。在大多数情况下,这是可行的,但它不匹配“th jn”(或“th john”),而是匹配“th”本身。

    我已经明确地将表达式中每个缩写词的外观从最长排列到最短,以避免出现这种情况,这依赖于正则表达式典型的贪婪行为。但是积极的前瞻性断言似乎使这一顺序短路,选择最短的匹配而不是最贪婪的匹配。

    当然,删除lookahead断言会使这个案例起作用,但会破坏许多其他测试。我该怎么解决这个问题呢?

    3 回复  |  直到 16 年前
        1
  •  1
  •   Alex Martelli    16 年前

    我已经放弃了一点尝试遵循什么 _sre.so 在这种情况下正在做(太复杂!)但我尝试的“盲法”似乎奏效了——切换到 消极的 互补字符集的先行断言…:

    cp = re.compile(ur"""
    (?:(
        # Numbered books
        (?:(?:Third|Thir|Thi|III|3rd|Th|3)\ ? 
           (?:John|Joh|Jhn|Jo|Jn|Jn|J))
        # Other books
        |Thessalonians|John|Th|Jn)\ ? 
      # Lookahead for numbers or punctuation
      (?![^\d:., ]))
    
    |
    

    等等,也就是说我改变了原版 (?=[\d:., ])) 正向先行变为“双重否定”形式(负向先行补码) (?![^\d:., ])) 这似乎消除了扰动。这对你来说行吗?

    我认为这是一个执行异常在这个角落的案例 如此 --在这两种情况下,看到其他的重新启动引擎会做什么可能是很有趣的,就像做一个健全性检查一样。

        2
  •  1
  •   chrissr    16 年前

    向前看并不是真正的短路。正则表达式在某种程度上是贪婪的。它更喜欢在第一个大块中进行匹配,因为它不想跨越“”边界到regex的第二部分,并且必须检查这一点。

    因为整个字符串不匹配第一个大块(因为lookeahead说它后面需要跟一个特定的字符而不是行尾),所以它只匹配“thessalonians”组中的“th”,lookahead在“th jn”中看到“th”后面有一个空格,所以它认为这是一个有效的匹配。

    你可能想做的是移动“帖撒罗尼迦人约翰th jn”)?分组到另一个大“”块。在课本的开头或结尾检查两个单词的书,或者在第三组中检查一个单词的书。

    希望这个解释有意义。

        3
  •  0
  •   David Eyk    16 年前

    我在问问题时发现的另一个替代解决方案是:切换块的顺序,先进行行末检查, 然后 最后是先行声明。但是,我更喜欢亚历克斯的双重否定的解决方案,并且已经实现了。