我有一个看起来像的字符串:
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'
1}}(它不会删除)。
以同样的方式,'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'
之间的双倍空格。
我尝试通过执行以下操作来组合这两个表达式:
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
这错误地删除了每个字,并完全忽略了'on\s'
或'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
这又错过了第二个雅芳。
答案 0 :(得分:3)
以下是解决此问题的两种方法:
将基于交替的lookbehind转换为几个负面的lookbehinds,因为它们之间的逻辑关系将是相同的(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))
请注意,这种方法很有用,因为您可以在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)