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

Posted

技术标签:

【中文标题】使用 re.findall 在正则表达式中捕获命名组【英文标题】:Capturing named groups in regex with re.findall 【发布时间】:2014-10-27 01:09:30 【问题描述】:

当我试图回答这个问题时:regex to split %ages and values in 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 pr re.match 时才能访问这些名称。根据我的阅读,findall 对元组中返回的组有一个固定的索引,问题是任何人都知道如何修改这些索引。这将有助于更轻松、更直观地处理组。

【问题讨论】:

不可能改变findall返回的组的顺序,但正如我在第二次展示的那样,事后重新排序很容易答案:***.com/a/25629693/20789 这就是我的假设,但找不到说明这一点的文档。因此我的问题在这里。 【参考方案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 ...) 而不是 dict 理解构造函数 (k:v for k,v in ...)。那是因为后者构造了dicttype 的实例,不能在不丢失键顺序的情况下将其转换为OrderedDict……这当然是您首先要保留的内容。

【讨论】:

我想知道是否可以指定或更改 re.findall 的原始退货顺序。转换为 dict 只是我想重新排序组的一个例子。 您的问题根本没有说明您要重新排序的内容。请对其进行编辑以澄清这一点。 更新: Python dict 确实为较新版本的 Python 保留键顺序(另见 SPEC SO Post)【参考方案2】:

根据this comment 中对 OP 意图的进一步说明,采取 3。

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

【讨论】:

【参考方案3】:

Per the OP's comment on my first answer:如果您只是想像这样重新排序 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 的顺序。 whatre-findall 的顺序?我正在向您展示如何获取re.findall 的输出并将其更改为您所说的您想要的顺序。 为了处理更大更复杂的正则表达式中的组,您可以命名组,但只有在执行 re.search pr re.match 时才能访问这些名称。根据我的阅读,findall 对元组中返回的组有一个固定的索引,问题是任何人都知道如何修改这些索引。这将有助于更轻松、更直观地处理组。 啊,命名组是一个单独的问题(也不是你的问题)。 findall 仅返回捕获的组并忽略名称是正确的;但是您可以简单地使用finditer 来返回匹配对象,通过它您将能够访问命名组。 那位先生,正是我要找的。如果您可以添加/修改您的答案,我会接受。谢谢

以上是关于使用 re.findall 在正则表达式中捕获命名组的主要内容,如果未能解决你的问题,请参考以下文章

正则表达式:匹配单个数字重复n次

正则表达式re.findall

是否有 Python 的 re.findall/re.finditer(迭代正则表达式结果)的 Perl 等价物?

python)使用正则表达式查找所有匹配项(从 re.search 更改为 re.findall)[重复]

通过正则表达式,获取股票数据(re.findall的应用)

python 正则(re.compile()/re.findall())