使用re.findall捕获正则表达式中的命名组

14 投票
3 回答
16390 浏览
提问于 2025-04-19 10:36

当我试图回答这个问题时:在Python中用正则表达式分割年龄和数值,我发现我需要重新排列从findall得到的结果中的组。例如:

data = """34% passed 23% failed 46% deferred"""
result = {key:value for value, key in re.findall('(\w+)%\s(\w+)', data)}
print(result)
>>> {'failed': '23', 'passed': '34', 'deferred': '46'}

这里findall的结果是:

>>> re.findall('(\w+)%\s(\w+)', data)
>>> [('34', 'passed'), ('23', 'failed'), ('46', 'deferred')]

有没有办法改变或指定这些组的顺序,让re.findall返回

[('passed', '34'), ('failed', '23'), ('deferred', '46')]

为了更清楚,我想说明一下,这个问题是:

是否可以指定顺序或重新排列re.findall函数返回的组?

我用上面的例子创建了一个字典,以提供一个理由或使用场景,说明你为什么想改变顺序(把键当成值,把值当成键)。

进一步说明:

为了处理更大更复杂的正则表达式中的组,你可以给组命名,但这些名字只有在使用re.search或re.match时才能访问。根据我所了解,findall返回的元组中的组有固定的索引,问题是有没有人知道这些索引怎么修改。这将有助于更轻松和直观地处理组。

3 个回答

1

根据提问者对我第一个回答的评论:如果你只是想重新排列一个包含二元组的列表,比如这样:

[('34', 'passed'), ('23', 'failed'), ('46', 'deferred')]

... 想要变成这样,每个元素的顺序都反过来:

[('passed', '34'), ('failed', '23'), ('deferred', '46')]

其实有个简单的办法:使用列表推导式和切片语法 sequence[::-1] 来反转每个元组里的元素顺序:

a = [('34', 'passed'), ('23', 'failed'), ('46', 'deferred')]
b = [x[::-1] for x in a]
print b
2

正如你在第二个例子中提到的,re.findall 会按照原来的顺序返回分组。

问题在于,标准的 Python dict 类型并不会保留键的顺序。在 Python 2.x 的手册中有明确说明,但在 Python 3.x 中也是如此:https://docs.python.org/2/library/stdtypes.html#dict.items

你应该使用的是 collections.OrderedDict

from collections import OrderedDict as odict

data = """34% passed 23% failed 46% deferred"""
result = odict((key,value) for value, key in re.findall('(\w+)%\s(\w+)', data))
print(result)
>>> OrderedDict([('passed', '34'), ('failed', '23'), ('deferred', '46')])

注意,你必须使用成对构造器的形式(dict((k,v) for k,v in ...),而不是使用 dict 的推导式({k:v for k,v in ...})。这是因为后者会创建 dict 类型的实例,而这种类型的实例在转换为 OrderedDict 时会丢失键的顺序……而这正是你最初想要保留的。

23

这是第三次尝试,基于提问者在这条评论中进一步澄清的意图。

Ashwin说得对,findall这个函数不会保留命名捕获组(比如(?P<name>regex))。不过,finditer可以帮忙!它会一个一个地返回匹配的对象。下面是一个简单的例子:

data = """34% passed 23% failed 46% deferred"""
for m in re.finditer('(?P<percentage>\w+)%\s(?P<word>\w+)', data):
    print( m.group('percentage'), m.group('word') )

撰写回答