前瞻断言似乎在正则表达式中对备选项进行短路排序

时间:2010-06-29 15:24:04

标签: python regex

我正在使用(Python风格的)正则表达式来识别经文引用的常见和特殊形式和缩写。给出以下详细摘录:

>>> 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”本身相匹配。

我已经命令表达式中每个缩写的出现从最长到最短,以避免出现这样的情况,依赖于正则表达式通常贪婪的行为。但积极的前瞻性断言似乎正在缩短这个顺序,选择最短的匹配而不是最贪婪的匹配。

当然,删除前瞻断言会使这种情况起作用,但会破坏其他一些测试。我该怎么办呢?

3 个答案:

答案 0 :(得分:1)

在我尝试跟踪_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:., ]))这似乎消除了扰动。这对你有效吗?

我认为这是_sre.so这个角落的实现异常 - 在这两种情况下看看其他RE引擎的作用可能会很有趣,就像进行健全性检查一样。

答案 1 :(得分:1)

前瞻并不是真正的短路。正则表达式只是贪得无厌。它会更喜欢你的第一个大块的匹配,因为它不想越过那个“|”正则表达式第二部分的边界,也必须检查。

由于整个字符串与第一个大块不匹配(因为lookeahead说它需要后跟一个特定字符而不是行尾),它只匹配“Thessalonians”组中的“Th”和前瞻在“Th Jn”中看到“Th”后面的空格,所以它认为这是一个有效的匹配。

您可能想要做的是将“| Thessalonians | John | Th | Jn)\?”组移到另一个大“|”块。检查文本开头或文本末尾的两本单词书,或检查第三组中的一本单词书。

希望这种解释有意义。

答案 2 :(得分:0)

我在问这个问题时发现的另一个替代解决方案:切换块的顺序,先将行尾检查,然后然后前瞻断言。但是,我更喜欢亚历克斯的双重否定解决方案,并已实现了这一点。