当我的模式只包含一个组时,为啥 re.findall 会返回一个元组列表?

Posted

技术标签:

【中文标题】当我的模式只包含一个组时,为啥 re.findall 会返回一个元组列表?【英文标题】:Why does re.findall return a list of tuples when my pattern only contains one group?当我的模式只包含一个组时,为什么 re.findall 会返回一个元组列表? 【发布时间】:2014-08-26 22:41:50 【问题描述】:

假设我有一个字符串s,其中包含字母和两个分隔符12。我想按以下方式拆分字符串:

如果子字符串t介于12之间,返回t 否则,返回每个字符

所以如果s = 'ab1cd2efg1hij2k',预期的输出是['a', 'b', 'cd', 'e', 'f', 'g', 'hij', 'k']

我尝试使用正则表达式:

import re
s = 'ab1cd2efg1hij2k'
re.findall( r'(1([a-z]+)2|[a-z])', s )

[('a', ''),
 ('b', ''),
 ('1cd2', 'cd'),
 ('e', ''),
 ('f', ''),
 ('g', ''),
 ('1hij2', 'hij'),
 ('k', '')]

从那里我可以通过[ x[x[-1]!=''] for x in re.findall( r'(1([a-z]+)2|[a-z])', s ) ] 得到我的答案,但我仍然不明白输出。 documentation 表示如果模式有多个组,findall 返回一个元组列表。但是,我的模式只包含一组。欢迎任何解释。

【问题讨论】:

【参考方案1】:

只需要做一个简单的更改:将组更改为非捕获组

代码:

import re
s = 'ab1cd2efg1hij2k'
re.findall( r'(1(?:[a-z]+)2|[a-z])', s )

输出:

['a', 'b', '1cd2', 'e', 'f', 'g', '1hij2', 'k']

【讨论】:

【参考方案2】:

如果您想在不拆分为匹配组的情况下进行“或”匹配,只需在“或”匹配的开头添加一个“?:”即可。

没有'?:'

re.findall('(test (word1|word2))', 'test word1')

Output:
[('test word1', 'word1')]

带'?:'

re.findall('(test (?:word1|word2))', 'test word1')

Output:
['test word1']

进一步解释:https://www.ocpsoft.org/tutorials/regular-expressions/or-in-regex/

【讨论】:

"non-capturing group" 是这里的关键字...(仅为搜索引擎添加)【参考方案3】:

我迟到了 5 年,但我想我可能已经找到了一个优雅的解决方案来解决 re.findall() 带有多个捕获组的丑陋的元组输出。

一般来说,如果你最终得到一个看起来像这样的输出:

[('pattern_1', '', ''), ('', 'pattern_2', ''), ('pattern_1', '', ''), ('', '', 'pattern_3')]

然后你可以用这个小技巧把它变成一个平面列表:

["".join(x) for x in re.findall(all_patterns, iterable)]

预期的输出将是这样的:

['pattern_1', 'pattern_2', 'pattern_1', 'pattern_3']

它在 Python 3.7 上进行了测试。希望对您有所帮助!

【讨论】:

【参考方案4】:

查看类似问题的答案:https://bugs.python.org/issue6663 如果您使用的是 findall,请去掉括号:

import re
s = 'ab1cd2efg1hij2k'
re.findall( r'(?<=1)[a-z]+(?=2)|[a-z]', s )

【讨论】:

【参考方案5】:

您的正则表达式有 2 个组,只需查看您使用的括号数 :)。一组是([a-z]+),另一组是(1([a-z]+)2|[a-z])。关键是您可以在其他组中拥有组。因此,如果可能,您应该构建一个只有一组的正则表达式,这样您就不必对结果进行后处理。

只有一组的正则表达式示例如下:

>>> import re
>>> s = 'ab1cd2efg1hij2k'
>>> re.findall('((?<=1)[a-z]+(?=2)|[a-z])', s)
['a', 'b', 'cd', 'e', 'f', 'g', 'hij', 'k']

【讨论】:

【参考方案6】:

你的模式有两个组,更大的组:

(1([a-z]+)2|[a-z])

和第二个较小的组,它是您的第一个组的 子集

([a-z]+)

这是一个可以为您提供预期结果的解决方案,但请注意,它真的很难看,可能有更好的方法。我就是想不通:

import re
s = 'ab1cd2efg1hij2k'
a = re.findall( r'((?:1)([a-z]+)(?:2)|([a-z]))', s )
a = [tuple(j for j in i if j)[-1] for i in a]

>>> print a
['a', 'b', 'cd', 'e', 'f', 'g', 'hij', 'k']

【讨论】:

你的模式很奇怪。您不需要围绕12 的非捕获组,或围绕整个模式的组(您需要花费大量精力来跳过输出)。相反,只需接受 findall 调用将返回 2 元组并使用 a = [x or y for x, y in a] 将它们转换为单个值。

以上是关于当我的模式只包含一个组时,为啥 re.findall 会返回一个元组列表?的主要内容,如果未能解决你的问题,请参考以下文章

ClassNotFoundException 解组时真的不知道为啥

当我写下原因时,我的 AFK 命令只显示一个字。为啥?

我的支付宝按钮不会链接到贝宝。它只刷新页面,为啥?

从字符串中提取数字 - 为啥在使用捕获组时会得到两个数组?

为啥当我没有为我的 openGLWidget 创建额外的类时 glRotate 只工作一次?

为什么“访问者”组中的用户无法访问我的SharePoint 2010发布网站。当我将它们推广到“成员”组时,它可以工作