Python正则表达式匹配电话号码异常:原因分析与修复方案
正则匹配电话号码的异常原因与修复方案
问题回顾
你尝试用Python正则表达式匹配电话号码,最初的实现代码如下:
import re r = re.compile(r'''(\+)*\d* # optional + sign for international calls ([\" "-\)]{,1}\d+)* # main chain of numbers, numbers separated by a space, ) or a hyphen ''',re.VERBOSE) print(r.findall('+00 0000 0000 is my number and +44-787-77950 was my uk number'))
你预期得到的结果是类似 [('+00',' 0000',' 0000'),('+44','-787','-77950')] 的分组元组,或是更简洁的 ['+00 0000 0000','+44-787-77950'],但实际返回了大量包含空字符串的异常元组:
[('+', ' 0000'), ('', ''), ('', ''), ('', ''), ('', ''), ('', ''), ('', ''), ('', ''), ('', ''), ('', ''), ('', ''), ('', ''), ('', ''), ('', ''), ('', ''), ('', ''), ('', ''), ('', ''), ('', ''), ('+', '44'), ('', ''), ('', '787'), ('', ''), ('', '77950'), ('', ''), ('', ''), ('', ''), ('', ''), ('', ''), ('', ''), ('', ''), ('', ''), ('', ''), ('', ''), ('', ''), ('', ''), ('', ''), ('', ''), ('', ''), ('', ''), ('', '')]
后续你调整了正则表达式:
import re r = re.compile(r'''(\+)?(\d+) # optional + sign for international calls ([\" "-\)]{,1}\d+)+ # main chain of numbers, numbers separated by a space, ) or a hyphen ''',re.VERBOSE) print(r.findall('+00 0000 0000 is my number and +44-787-77950 was my uk number'))
但结果依然不符合预期:
[('+', '00', ' 0000'), ('+', '4', '4'), ('', '78', '7'), ('', '7795', '0')]
异常原因分析
咱们一步步拆解问题根源:
第一个正则的核心问题:
- 你使用了
*(零次或多次匹配)修饰捕获分组,比如(\+)*和([\" "-\)]{,1}\d+)*。注意:re.findall的规则是,如果正则包含捕获分组,会返回分组的匹配结果列表;若分组允许零次匹配,就会产生大量空字符串的捕获结果。 \d*会匹配零个数字,导致正则会在文本的空白、非数字字符处强制匹配,进一步增加了空结果的数量。- 字符集
[\" "-\)]存在语法问题:单引号包裹的正则字符串里不需要转义双引号,且-放在字符集中间会被解析为范围符号,正确写法应为[ -)](把-放在开头或结尾,避免被识别为范围)。
- 你使用了
第二个正则的核心问题:
- 虽然把
*改成了+(至少匹配一次),但([\" "-\)]{,1}\d+)+是重复的捕获分组。正则中重复的捕获分组只会保留最后一次匹配的内容,导致原本连续的数字段被错误拆分(比如44被拆成4和4)。 - 没有对整个电话号码做整体匹配限制,导致正则会匹配文本中的零散数字片段,而非完整的号码。
- 虽然把
修复方案
根据你的两种预期结果,分别给出针对性的解决方案:
方案1:获取完整的电话号码字符串(推荐)
如果你想要['+00 0000 0000','+44-787-77950']这种简洁结果,我们需要让正则匹配整个电话号码,并使用非捕获分组避免多余的分组捕获:
import re r = re.compile(r''' \+? # 可选的+号,满足国际区号可选需求 \d+ # 号码开头的数字(至少1位,避免零匹配) (?:[ -)]\d+)+ # 非捕获分组:匹配分隔符(空格/连字符/右括号)+数字,至少重复1次 ''', re.VERBOSE) print(r.findall('+00 0000 0000 is my number and +44-787-77950 was my uk number'))
运行结果:
['+00 0000 0000', '+44-787-77950']
方案2:获取拆分后的号码段元组
如果你想要[('+00',' 0000',' 0000'),('+44','-787','-77950')]的分组结果,需要先捕获完整的号码段,再拆分后续部分:
import re r = re.compile(r''' (\+?\d+) # 捕获带+号的开头数字段(如+00、+44) ((?:[ -)]\d+)+) # 捕获后续所有数字段的整体内容 ''', re.VERBOSE) matches = r.findall('+00 0000 0000 is my number and +44-787-77950 was my uk number') # 拆分后续段并组合成目标格式 result = [] for start, rest in matches: parts = [start] + re.findall(r'[ -)]\d+', rest) result.append(tuple(parts)) print(result)
运行结果:
[('+00', ' 0000', ' 0000'), ('+44', '-787', '-77950')]
内容的提问来源于stack exchange,提问作者user160738
相关产品推荐
相关产品推荐

