Python 3.4.0 正则匹配手机号findall返回空串及与regex101结果差异咨询
首先明确Python re 模块中 findall 的固定返回逻辑:当正则表达式中包含带括号的捕获分组(即未加 ?: 标识的非捕获语法)时,findall 不会只返回每个位置上匹配到的完整字符串,而是按照正则书写的顺序,返回每个匹配项下所有捕获组的内容列表——无论这个捕获组是否实际匹配到了源字符串的字符。
为什么匹配结果中会包含空字符串?
你编写的正则一共定义了9个捕获分组,每个带()的结构都会按顺序占据findall结果中的一个固定位置,从外层到内层依次对应匹配逻辑:
- 包裹整个手机号匹配流程的最外层分组
- 支持括号格式的3位区号分组
- 区号后的可选分隔符分组
- 号码前3位数字分组
- 号码中间的固定分隔符分组
- 号码后4位数字分组
- 完整的分机号匹配块分组
- 分机标识(ext/x/ext.)分组
- 分机数字分组
其中分机相关的3个捕获组,本身是用?标记的可选匹配规则,你用来测试的源字符串里完全没有分机号的相关内容,这三个组没有实际匹配到任何字符,Python的re引擎会给这三个未命中的可选分组补一个空字符串占位,最终就形成了你看到的连续3个''。
这些空字符串对应源字符串中的什么位置?
这些空值没有对应源字符串里的任何实际字符位置,它只是Python正则引擎为了保持捕获组位置一致性生成的占位值。
就像给数据表单预设了三个分机号填写栏,哪怕填写人根本没填这些栏,导出数据时也会在这三栏的位置留空,不会直接把栏位删掉。你匹配到的两个手机号结束的位置,本身已经没有后续的分机内容了,这三个空值对应的是“本该匹配分机内容的分组槽位”,不是源字符串里真实存在的字符。
满足什么条件时,正则会匹配出空字符串?
通常两种场景会在findall的返回结果里出现空字符串:
- 第一种就是你当前遇到的场景:正则中定义了带
?的可选捕获组,匹配时这个组对应的规则在当前位置的源串中不存在,引擎跳过匹配后会给这个组返回空值占位。 - 第二种是正则规则本身的匹配范围允许0长度匹配:比如分组规则是
(\d*)、(\s*)这类可以匹配0个字符的表达式,哪怕源串里没有对应内容,分组也会在当前匹配位置返回空字符串。
你在正则在线测试站点上使用完全相同的规则时,匹配结果没出现这些空值,本质是这类站点的默认展示逻辑会自动折叠隐藏未命中的可选捕获组,不会把空占位值列在展示列表里,和Python
re模块的严格返回逻辑存在展示层面的差异,不是正则规则本身有区别。如果想让返回结果更简洁,可以把不需要单独提取内容的捕获组改成非捕获组(在括号开头加?:),引擎就不会把这些组纳入findall的返回列表。
内容的提问来源于stack exchange,提问作者Fateen

