为何Python的re.findall返回子字符串元组而非完整匹配字符串?
为什么re.findall返回元组而非完整匹配的字符串?
我来帮你拆解这个问题——这其实是re.findall的一个核心行为规则,和你正则里的捕获组直接相关。
先重现一下你的问题场景:
import re regex = r'((?:\+|00)[17](?: |\-)?|(?:\+|00)[1-9]\d{0,2}(?: |\-)?|(?:\+|00)1\-\d{3}(?: |\-)?)?(0\d|\([0-9]{3}\)|[1-9]{0,3})(?:((?: |\-)[0-9]{2}){4}|((?:[0-9]{2}){4})|((?: |\-)[0-9]{3}(?: |\-)[0-9]{4})|([0-9]{7}))' # 测试空文本 print(re.findall(regex, "some text")) # 输出: [] # 测试带号码的文本 print(re.findall(regex, "phone: +447825492609")) # 输出: [('+447', '82', '', '', '', '5492609')]
问题原因
re.findall的工作逻辑很明确:
- 如果你的正则表达式中包含一个或多个捕获组(就是那些没有加
?:的()),它会优先返回这些捕获组匹配到的内容,把每个匹配对应的所有捕获组结果拼成一个元组,最终返回元组的列表。 - 只有当正则里完全没有捕获组时,它才会返回完整匹配的字符串列表。
你的正则里用了好几个捕获组(比如开头的((?:\+|00)[17]...)、中间的(0\d...),还有后面分支里的((?: |\-)[0-9]{2}){4}等),所以findall会把每个捕获组单独提取的内容组合成元组返回,而不是你预期的完整号码字符串。
解决方案
有两种简单的办法可以得到你想要的结果:
方法1:把所有捕获组改为非捕获组
把正则里所有不需要单独提取的()都改成(?:...)(非捕获组,只用于分组匹配,不会被findall单独提取),这样整个正则就没有捕获组了,findall就会返回完整匹配的字符串:
import re # 修改后的正则:所有原捕获组都改成非捕获组 regex = r'(?:(?:\+|00)[17](?: |\-)?|(?:\+|00)[1-9]\d{0,2}(?: |\-)?|(?:\+|00)1\-\d{3}(?: |\-)?)?(?:0\d|\([0-9]{3}\)|[1-9]{0,3})(?:(?:(?: |\-)[0-9]{2}){4}|(?:(?:[0-9]{2}){4})|(?:(?: |\-)[0-9]{3}(?: |\-)[0-9]{4})|(?:[0-9]{7}))' print(re.findall(regex, "phone: +447825492609")) # 输出: ['+447825492609']
方法2:拼接捕获组结果(适合需要保留捕获组的场景)
如果你还需要保留原有的捕获组用于其他操作,可以把所有捕获组的结果拼接起来,还原成完整的号码:
import re regex = r'((?:\+|00)[17](?: |\-)?|(?:\+|00)[1-9]\d{0,2}(?: |\-)?|(?:\+|00)1\-\d{3}(?: |\-)?)?(0\d|\([0-9]{3}\)|[1-9]{0,3})(?:((?: |\-)[0-9]{2}){4}|((?:[0-9]{2}){4})|((?: |\-)[0-9]{3}(?: |\-)[0-9]{4})|([0-9]{7}))' matches = re.findall(regex, "phone: +447825492609") # 把每个元组里的非空字符串拼接起来 full_numbers = [''.join(filter(None, match)) for match in matches] print(full_numbers) # 输出: ['+447825492609']
内容的提问来源于stack exchange,提问作者user68790678298
相关产品推荐
相关产品推荐

