Python正则表达式负向回顾后发现未匹配成功

3
我正在编写一个正则表达式来匹配电话号码。我遇到的问题之一是有些邮政编码看起来像电话号码。例如,在巴西,邮政编码长这样:
30.160-0131

因此,一个简单的正则表达式将捕获它们作为误报:

In [63]: re.search(r"(?P<phone>\d+\.\d+-\d+)", "30.160-0131")
Out[63]: <_sre.SRE_Match at 0x102150990>

幸运的是,这样的邮政编码通常带有前缀,一般意味着“邮政编码”,就像这样:


CEP 30.160-0131

因此,如果您看到CEP前面有像电话号码一样的东西,那么它不是电话号码 - 它是邮政编码。我一直在尝试使用负向回顾后发断言编写正则表达式来捕获它,但它没有起作用。它仍然匹配:

In [62]: re.search(r"(?<!CEP )(\d+\.\d+-\d+)", "CEP 30.160-0131")
Out[62]: <_sre.SRE_Match at 0x102150eb8>

为什么还会匹配成功,如何让负向后瞻失败从而不匹配呢?

1
因为部分可以匹配:http://regex101.com/r/aU4fV4/1 - jonrsharpe
2个回答

3
表达式匹配是因为您没有对数字进行任何锚定。例如:
"CEP 11.213-132"

如果不紧跟在CEP之后,它将匹配1.213-132。但是你可以强制一个空格或行首锚点位于第一个数字之前:

re.search(r"(?<!CEP)(?:\s+|^)(\d+\.\d+-\d+)", s)

但是如果没有前缀,例如 re.search(r"(?<!CEP)\s+(\d+\.\d+-\d+)", "30.160-0131") 将匹配不到任何内容,返回 None - mpenkov
谢谢!你的解决方案非常有效,但最终我选择了@Jerry的想法,因为它更简单,更容易理解(对我来说)。 - mpenkov

1
如果您允许匹配那些邮政编码,仍然可以提取电话号码,从而避免使用负向先行断言:
m = re.search(r"CEP \d+\.\d+-\d+|(\d+\.\d+-\d+)", s)

然后检查在电话号码中是否有m.group(1)的内容。


使用 re.findall 的小演示:

>>> import re
>>> s = "There is a CEP 30.160-0131 and a  30.160-0132 in that sentence, which repeats itself like there is a CEP 30.160-0131 and a  30.160-0132 in that sentence."
>>> m = re.findall(r"CEP \d+\.\d+-\d+|(\d+\.\d+-\d+)", s)
>>> print(m)
['', '30.160-0132', '', '30.160-0132']

从那里,你可以过滤掉空字符串。

1
@misha 在你通常使用的代码中没有访问后顾之忧,这对解决问题很有帮助 ;) - Jerry

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接