我有一个看起来像的字符串:

phrase = '5 hampshire road bradford on avon avon dinas powys powys north somerset hampshire avon'


我想返回一个删除了某些单词的新字符串,前提是它们前面没有某些其他单词。

例如,我要删除的单词是:

c_out = ["avon", "powys", "somerset","hampshire"]


仅当他们不遵循时:

c_except = ["on\s","dinas\s"]


注意:c_out中可能有多个单词实例,而c_except中可能有多个单词实例。

我分别尝试了'on\s'

phrase = '5 hampshire road bradford on avon avon dinas powys powys north somerset hampshire avon'

regexp1 = re.compile(r'(?<!on\s)(avon|powys|somerset|hampshire)')
print("1st Result: ", regexp1.sub('', phrase))
1st Result:  '5  road bradford on avon avon dinas   north'


这正确地忽略了第一个'avon',因为它以'on\s'开头,它正确地删除了第三个'avon',但是它忽略了第二个'avon'(不会删除)。

以相同的方式,对于'dinas\s'

phrase = '5 hampshire road bradford on avon avon dinas powys powys north somerset hampshire avon'

regexp2 = re.compile(r'(?<!dinas\s)(avon|powys|somerset|hampshire)')
print("2nd Result: ", regexp2.sub('', phrase))
2nd Result:  '5  road bradford on   dinas powys  north '


这样可以正确忽略第一个'powys'并删除第二个'... powys north'(请注意'on\s'之间的双倍空格。

我尝试通过以下方式将两个表达式组合在一起:

regexp3 = re.compile(r'((?!on\s)|(?!dinas\s))(avon|powys|somerset|hampshire)')
print("3rd Result: ", regexp3.sub('', phrase))
3rd Result:  5  road bradford on   dinas   north


这错误地删除了每个单词,并完全忽略了'dinas\s'或。

然后我尝试了:

regexp4 = re.compile(r'(?<!on\s|dinas\s)(avon|powys|somerset|hampshire)')
print("4th Result: ", regexp4.sub('', phrase))


并得到:

error: look-behind requires fixed-width pattern


我要结束于:

Result: '5  road bradford on avon dinas powys  north     '


我看了看:

Why is this not a fixed width pattern?
Python Regex Engine - "look-behind requires fixed-width pattern" Error
regex: string with optional parts

但无济于事。

我究竟做错了什么?



来自评论:

regexp5 = re.compile(r'(?<!on\s)(?<!dinas\s)(avon|powys|somerset|hampshire)')
print("5th Result: ", regexp5.sub('', phrase))
5th Result:  5  road bradford on avon avon dinas powys  north


再次错过了第二个雅芳。

最佳答案

这是解决问题的两种方法:
链式环视
将基于交替的lookbehins转换为几个否定的lookbehins,因为它们之间的逻辑关系相同(AND的逻辑关系):

import re
phrase = '5 hampshire road bradford on avon avon dinas powys powys north somerset hampshire avon'
c_except = [r"on\s",r"dinas\s"]
c_out = ["avon", "powys", "somerset","hampshire"]
rx = r"(?<!\b{0})({1})".format(r")(?<!\b".join(c_except), "|".join(c_out))
print(re.sub(rx, "", phrase))

请参见this Python demo
捕获方法
捕获您需要保留的内容并仅匹配您需要删除的内容,然后使用\1后向引用来恢复组1的值:
import re
phrase = '5 hampshire road bradford on avon avon dinas powys powys north somerset hampshire avon'
c_except = [r"on\s+",r"dinas\s+"]
c_out = ["avon", "powys", "somerset","hampshire"]
rx = r"(\b(?:{0})(?:{1}))|(?:{1})".format(r"|".join(c_except), "|".join(c_out))
print(re.sub(rx, r"\1", phrase))

请参见another Python demo
请注意,这种方法是有利的,因为您可以在c_except中使用可变宽度的图案。
正则表达式看起来像
(\b(?:on\s+|dinas\s+)(?:avon|powys|somerset|hampshire))|(?:avon|powys|somerset|hampshire)

由于on词的边界,它将匹配dinas\b整个词,然后匹配您需要删除的任何术语,并且由于该部分被包装到捕获组中,因此可以使用\1后向引用。在所有其他情况下,将使用c_out模式删除|(?:avon|powys|somerset|hampshire)术语。
注意:\1替换将在Python 3.5+中工作。对于旧版本和Python 2.x,您需要将其替换为lambda:
re.sub(rx, lambda m: m.group(1) if m.group(1) else "", phrase)

08-27 14:54
查看更多