使用 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 ...
)。那是因为后者构造了dict
type 的实例,不能在不丢失键顺序的情况下将其转换为OrderedDict
……这当然是您首先要保留的内容。
【讨论】:
我想知道是否可以指定或更改 re.findall 的原始退货顺序。转换为 dict 只是我想重新排序组的一个例子。 您的问题根本没有说明您要重新排序的内容。请对其进行编辑以澄清这一点。 更新: Pythondict
确实为较新版本的 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 的顺序。 what 到re-findall
的顺序?我正在向您展示如何获取re.findall
的输出并将其更改为您所说的您想要的顺序。
为了处理更大更复杂的正则表达式中的组,您可以命名组,但只有在执行 re.search pr re.match 时才能访问这些名称。根据我的阅读,findall 对元组中返回的组有一个固定的索引,问题是任何人都知道如何修改这些索引。这将有助于更轻松、更直观地处理组。
啊,命名组是一个单独的问题(也不是你的问题)。 findall
仅返回捕获的组并忽略名称是正确的;但是您可以简单地使用finditer
来返回匹配对象,通过它您将能够访问命名组。
那位先生,正是我要找的。如果您可以添加/修改您的答案,我会接受。谢谢以上是关于使用 re.findall 在正则表达式中捕获命名组的主要内容,如果未能解决你的问题,请参考以下文章
是否有 Python 的 re.findall/re.finditer(迭代正则表达式结果)的 Perl 等价物?