Python-正则表达式搜索和查找所有。

15

我需要找到给定正则表达式在字符串中的所有匹配项。我一直使用findall()来做这件事情,直到遇到一个它无法按照我的预期工作的案例。例如:

regex = re.compile('(\d+,?)+')
s = 'There are 9,000,000 bicycles in Beijing.'

print re.search(regex, s).group(0)
> 9,000,000

print re.findall(regex, s)
> ['000']

在这种情况下,search() 返回我需要的内容(最长匹配),但findall() 表现不同,即使文档暗示应该相同:

findall() 匹配所有模式的出现,而不仅仅是像 search() 那样只匹配第一个。

  • 为什么行为不同?

  • 如何通过 findall()(或其他方式)实现与 search() 相同的结果?

2个回答

19

好的,我明白发生了什么......从文档中得知:

如果模式中存在一个或多个组,则返回一个由这些组成的列表; 如果模式具有多个组,则这将是元组的列表。

事实证明,你确实有一个组"(\d+,?)"...所以,它返回的是该组的最后一个出现,即000。

一种解决方案是用一个组包围整个正则表达式,像这样:

regex = re.compile('((\d+,?)+)')

然后,它将返回[('9,000,000', '000')],这是一个包含两个匹配组的元组。当然,你只关心第一个。

个人而言,我会使用以下正则表达式

regex = re.compile('((\d+,)*\d+)')

为了避免匹配类似于“ this is a bad number 9,123,”的内容,

编辑。

这里有一个方法可以避免将表达式括在括号中或处理元组。

s = "..."
regex = re.compile('(\d+,?)+')
it = re.finditer(regex, s)

for match in it:
  print match.group(0)

finditer返回一个迭代器,您可以使用它来访问所有找到的匹配项。这些匹配对象与re.search返回的相同,因此group(0)返回您期望的结果。


感谢您的解释。正如您所建议的那样,finditer 实际上更适合我正在做的事情。正则表达式来自用户输入,因此我无法控制它。 - armandino

7

@aleph_null的回答正确地解释了你的问题原因,但我认为我有更好的解决方案。使用这个正则表达式:

regex = re.compile(r'\d+(?:,\d+)*')

一些原因,说明为什么这样更好:
  1. (?:...) 是一个非捕获组,所以每次匹配只会得到一个结果。

  2. \d+(?:,\d+)* 是一个更好的正则表达式,更有效率,且不太可能返回错误的结果。

  3. 如果可能的话,您应该始终使用 Python 的原始字符串来处理正则表达式;这样可以减少由于正则表达式转义序列(例如用于单词边界的 \b)被解释为字符串文字转义序列(例如用于退格键的 \b)而导致的错误发生。


谢谢Alan!我之前应该提到,但是我无法控制正则表达式,因为它是用户输入的。 - armandino
2
没问题!但是,为了记录,让用户输入正则表达式并由您的应用程序执行是一个坏主意。当他们编写不好(或仅仅是匆忙输入)的正则表达式无法匹配或导致系统崩溃时,他们会责怪 的。 ;) - Alan Moore

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