我有一个看起来像的字符串:
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)