re.finditer和re.findall的行为差异

28 投票
7 回答
87317 浏览
提问于 2025-04-16 04:26

我正在使用以下代码:

CARRIS_REGEX=r'<th>(\d+)</th><th>([\s\w\.\-]+)</th><th>(\d+:\d+)</th><th>(\d+m)</th>'
pattern = re.compile(CARRIS_REGEX, re.UNICODE)
matches = pattern.finditer(mailbody)
findall = pattern.findall(mailbody)

但是,finditer和findall找到的结果却不一样。findall确实能找到给定字符串中的所有匹配项。但finditer只找到第一个匹配项,返回的只是一个包含一个元素的迭代器。

我该如何让finditer和findall的行为一致呢?

谢谢

7 个回答

7

我从正则表达式操作Python 2.* 文档中获取了这个例子,并对其进行了详细的描述和一些修改。为了更好地解释这个例子,我们先定义一个字符串类型的变量,

text = "He was carefully disguised but captured quickly by police."

然后使用compile类型的正则表达式模式,

regEX = r"\w+ly"
pattern = re.compile(regEX)

\w表示匹配任何单词字符(字母数字和下划线)+表示匹配前面字符一次或多次,整个意思就是选择以 ly 结尾的任何单词。只有两个单词('carefully' 和 'quickly')符合上述正则表达式。

在我们深入了解re.findall()re.finditer()之前,先看看re.search()Python 2.* 文档中的含义。

扫描字符串,寻找正则表达式模式匹配的第一个位置,并返回相应的 MatchObject 实例。如果字符串中没有位置匹配该模式,则返回 None;注意,这与在字符串中的某个点找到零长度匹配是不同的。

接下来的代码行将帮助你基本理解re.search()

search = pattern.search(text)
print(search)
print(type(search))

#output
<re.Match object; span=(7, 16), match='carefully'>
<class 're.Match'>

它会生成一个re.MatchObject对象,这个对象有13个支持的方法和属性,具体可以参考Python 2.* 文档。这个span()方法包含了匹配单词在text变量中的起始和结束位置(在上面的例子中是7和16)。re.search()方法只关注第一个匹配,否则返回None

接下来我们要进入问题之前,先看看re.finditer()Python 2.* 文档中的含义。

返回一个迭代器,生成字符串中所有不重叠的正则表达式模式匹配的 MatchObject 实例。字符串是从左到右扫描的,匹配结果按找到的顺序返回。空匹配也包含在结果中。

接下来的代码行将帮助你基本理解re.finditer()

finditer = pattern.finditer(text)
print(finditer)
print(type(finditer))

#output
<callable_iterator object at 0x040BB690>
<class 'callable_iterator'>

上面的例子给我们提供了需要循环的迭代器对象。这显然不是我们想要的结果。让我们循环一下finditer,看看这个迭代器对象里面有什么。

for anObject in finditer:
    print(anObject)
    print(type(anObject))
    print()

#output
<re.Match object; span=(7, 16), match='carefully'>
<class 're.Match'>

<re.Match object; span=(40, 47), match='quickly'>
<class 're.Match'>

这些结果与我们之前得到的re.search()结果非常相似。但我们可以在上面的输出中看到新的结果,<re.Match object; span=(40, 47), match='quickly'>。正如我之前提到的,在Python 2.* 文档中,re.search()扫描字符串,寻找正则表达式模式匹配的第一个位置,而re.finditer()扫描字符串,寻找正则表达式模式匹配的所有位置,并返回比re.findall()方法更多的细节。

接下来看看re.findall()Python 2.* 文档中的含义。

返回字符串中模式的所有不重叠匹配,作为一个字符串列表。字符串是从左到右扫描的,匹配结果按找到的顺序返回。如果模式中有一个或多个组,则返回一个组的列表;如果模式有多个组,则返回一个元组列表。空匹配也包含在结果中。

让我们理解一下re.findall()的工作原理。

findall = pattern.findall(text)
print(findall)
print(type(findall))

#output
['carefully', 'quickly']
<class 'list'>

这个输出只给我们在text变量中匹配的单词,否则返回一个空的列表。这个输出中的列表类似于re.MatchObject中的match属性。

这是我在Python 3.7中尝试的完整代码。

import re

text = "He was carefully disguised but captured quickly by police."

regEX = r"\w+ly"
pattern = re.compile(regEX)

search = pattern.search(text)
print(search)
print(type(search))
print()

findall = pattern.findall(text)
print(findall)
print(type(findall))
print()

finditer = pattern.finditer(text)
print(finditer)
print(type(finditer))
print()
for anObject in finditer:
    print(anObject)
    print(type(anObject))
    print()
7

re.findall(pattern.string)

findall() 会在字符串中找到所有不重叠的匹配项,并把它们以字符串列表的形式返回。

re.finditer()

finditer() 会返回一个可调用对象

在这两个函数中,字符串是从左到右扫描的,找到的匹配项会按照发现的顺序返回。

43

我在这里无法重现这个问题。我尝试过使用Python 2.7和3.1。

finditerfindall之间的一个区别是,前者返回的是正则表达式匹配对象,而后者返回的是匹配到的捕获组的元组(如果没有捕获组,则返回整个匹配结果)。

所以

import re
CARRIS_REGEX=r'<th>(\d+)</th><th>([\s\w\.\-]+)</th><th>(\d+:\d+)</th><th>(\d+m)</th>'
pattern = re.compile(CARRIS_REGEX, re.UNICODE)
mailbody = open("test.txt").read()
for match in pattern.finditer(mailbody):
    print(match)
print()
for match in pattern.findall(mailbody):
    print(match)

会打印出

<_sre.SRE_Match object at 0x00A63758>
<_sre.SRE_Match object at 0x00A63F98>
<_sre.SRE_Match object at 0x00A63758>
<_sre.SRE_Match object at 0x00A63F98>
<_sre.SRE_Match object at 0x00A63758>
<_sre.SRE_Match object at 0x00A63F98>
<_sre.SRE_Match object at 0x00A63758>
<_sre.SRE_Match object at 0x00A63F98>

('790', 'PR. REAL', '21:06', '04m')
('758', 'PORTAS BENFICA', '21:10', '09m')
('790', 'PR. REAL', '21:14', '13m')
('758', 'PORTAS BENFICA', '21:21', '19m')
('790', 'PR. REAL', '21:29', '28m')
('758', 'PORTAS BENFICA', '21:38', '36m')
('758', 'SETE RIOS', '21:49', '47m')
('758', 'SETE RIOS', '22:09', '68m')

如果你想让finditer的输出和findall的一样,你需要

for match in pattern.finditer(mailbody):
    print(tuple(match.groups()))

撰写回答