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

16 投票
2 回答
24514 浏览
提问于 2025-04-17 06:12

我需要在一个字符串中找到所有符合给定正则表达式的匹配项。我一直在用 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 个回答

7

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

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

以下是为什么这个方案更好的几个原因:

  1. (?:...)是一个不捕获的分组,这样每次匹配时你只会得到一个结果。

  2. \d+(?:,\d+)*是一个更好的正则表达式,它更高效,也更不容易出现错误匹配的情况。

  3. 如果可能的话,使用Python的原始字符串来写正则表达式;这样你就不容易被正则表达式中的转义字符(比如\b表示“单词边界”)搞混成字符串中的转义字符(比如\b表示“退格”)。

20

好的,我明白发生了什么……根据文档:

如果模式中有一个或多个分组,就会返回一个分组的列表;如果模式有多个分组,这将是一个元组的列表。

实际上,你确实有一个分组,“(\d+,?)”……所以,它返回的是这个分组的最后一个匹配结果,或者说是000。

一个解决办法是把整个正则表达式用一个分组包起来,像这样:

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

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

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

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

这样可以避免匹配到像“这是一个坏数字 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) 会返回你期待的结果。

撰写回答