当我的模式只包含一个组时,为啥 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
,其中包含字母和两个分隔符1
和2
。我想按以下方式拆分字符串:
t
介于1
和2
之间,返回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']
【讨论】:
你的模式很奇怪。您不需要围绕1
和2
的非捕获组,或围绕整个模式的组(您需要花费大量精力来跳过输出)。相反,只需接受 findall
调用将返回 2 元组并使用 a = [x or y for x, y in a]
将它们转换为单个值。以上是关于当我的模式只包含一个组时,为啥 re.findall 会返回一个元组列表?的主要内容,如果未能解决你的问题,请参考以下文章
ClassNotFoundException 解组时真的不知道为啥