Python - 错误:look-behind需要固定宽度模式

时间:2017-08-03 09:01:38

标签: python regex python-3.x

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

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 

这又错过了第二个雅芳。

1 个答案:

答案 0 :(得分:3)

以下是解决此问题的两种方法:

Chained Lookbehinds

将基于交替的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

捕获Approch

捕获您需要保留的内容并仅匹配您需要删除的内容,并使用\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)