为什么re.findall在模式中只有一个组时返回元组列表?
假设我有一个字符串 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 ) ]
来得到我的答案,但我还是不太明白输出的结果。文档说 findall
如果模式有多个组,就会返回一个元组的列表。然而,我的模式只有一个组。欢迎任何解释。
6 个回答
看看这个回答,它是针对类似问题的: https://bugs.python.org/issue6663 如果你在使用findall的时候,可以直接去掉括号。
import re
s = 'ab1cd2efg1hij2k'
re.findall( r'(?<=1)[a-z]+(?=2)|[a-z]', s )
如果你想要在匹配中使用“或”这个功能,但又不想把结果分成不同的匹配组,只需要在“或”匹配的开头加上“?:”就可以了。
没有“?:”的情况
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/
你的正则表达式里有两个组,看看你用了多少个括号就知道了 :)一个组是 ([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']
我虽然晚了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上测试过的。希望对你有帮助!
你的模式有两个部分,较大的部分:
(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']