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

14

当我试图回答这个问题: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'}

查找所有匹配项的结果如下:

>>> 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中的组具有固定的索引返回到元组中,问题是是否有人知道如何修改那些索引。这将有助于使组的处理更加轻松和直观。


1
无法更改findall返回的组的顺序,但是很容易在事后重新排序它们,就像我在我的第二个答案中展示的那样:https://dev59.com/sV8e5IYBdhLWcg3wja75#25629693 - Dan Lenski
1
这是我假设的,但找不到文件来证明。因此我在这里提出了问题。 - ashwinjv
3个回答

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') )

1

正如您在第二个示例中所指出的那样,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 ...)而不是字典推导式构造函数({k:v for k,v in ...})。这是因为后者构造出的实例是dict类型,无法转换为OrderedDict而不丢失键的顺序...这正是您首先要保留的。

我在想是否可以指定或更改re.findall返回结果的原始顺序。将其转换为字典只是我想重新排序组时的示例。 - ashwinjv
你的问题并没有清楚地表明你想要重新排序什么。请编辑以澄清此事。 - Dan Lenski
2
更新:Python dict 对于较新版本的 Python 确实 保留了键的顺序(另请参阅 SPEC SO Post)。 - dreftymac

1

根据提问者在我第一个回答中的评论所述:如果您只是想像这样重新排序一个由2元组组成的列表:

[('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

我知道如何重新排序元组,问题是指定re.findall的顺序。 - ashwinjv
重新查找的顺序是什么?我将向您展示如何获取re.findall的输出并更改其顺序,以符合您所需的顺序。 - Dan Lenski
1
为了处理更大更复杂的正则表达式中的组,您可以对组进行命名,但只有在执行re.search或re.match时才能访问这些名称。据我所知,findall在元组中返回的组具有固定的索引。问题是,是否有人知道如何修改这些索引。这将有助于使组的处理更加容易和直观。 - ashwinjv
这里的文档https://docs.python.org/3.1/library/re.html#re.findall说你会得到一个带组的元组列表,但没有讨论该元组中组的索引。 - ashwinjv
1
啊,命名组是一个单独的问题(也不在你的问题中)。你正确地指出findall仅返回捕获的组并忽略名称;但是,你可以简单地使用finditer代替来返回匹配对象,从而可以访问命名组。 - Dan Lenski
1
先生,那正是我在寻找的。如果你能添加/修改你的回答,我会接受它。谢谢。 - ashwinjv

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