我正在使用(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”本身相匹配。
我已经命令表达式中每个缩写的出现从最长到最短,以避免出现这样的情况,依赖于正则表达式通常贪婪的行为。但积极的前瞻性断言似乎正在缩短这个顺序,选择最短的匹配而不是最贪婪的匹配。
当然,删除前瞻断言会使这种情况起作用,但会破坏其他一些测试。我该怎么办呢?
答案 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)
我在问这个问题时发现的另一个替代解决方案:切换块的顺序,先将行尾检查,然后然后前瞻断言。但是,我更喜欢亚历克斯的双重否定解决方案,并已实现了这一点。