分享一个我维护了几年的匹配国内手机号码的正则表达式列表

2019-01-07 14:16:17 +08:00
 vincentxue



项目地址是: https://github.com/VincentSit/ChinaMobilePhoneNumberRegex

列了一些常用的情况,例如匹配所有号码、所有支持短信功能的号码,包括手机卡、数据卡( IoT )和上网卡以及细分运营商匹配等。

初衷是由于国家一直在放出和使用新的号段,有些使用新的号段的用户无法通过手机号码的正则验证,网上很多正则并不能及时地更新,去找了下开源的项目也没人做这个事,我就自己写了一个。

最近更新支持了最新的号段,做了有限的测试,发到这里一是考虑自身水平有限,请各位大佬提提建议,找找 bug,二是广而告之,希望能帮助到有需要的同学。

关于测试方面,我的设想是想给每个常用语言做一个开源库包含这些正则,该语言相关的测试会在库源码里做。这样使用起来就可以直接引用库封装好的代码,更省事一些。但是我实现了一个语言后发现这是个大坑,得慢慢填。

需要说明的是因为考虑到方便使用的原因,有些正则其实并不是传统意义上极致优化的,可以根据自身需要去修改。例如不需要匹配国家码,可以去掉匹配国家码的那一段。但除非匹配量非常巨大,否则这个性能差别是可以忽略的。我在我机器上用 PHP 7 测了一下匹配一百多万行的手机号码,性能差距还是能接受的。
7713 次点击
所在节点    分享创造
61 条回复
xxxiong
2019-01-08 09:34:01 +08:00
支持,也是有心了
qingxiangcool
2019-01-08 09:35:02 +08:00
已 star
RyougiShiki
2019-01-08 10:15:06 +08:00
原来公司业务需求匹配某个省的移动号,写正则挺费心的。
vincentxue
2019-01-08 10:53:37 +08:00
@RyougiShiki 你这个需求用数据库可能更合适啊,正则感觉不是很适合啊。不过主要还是要有规则,有了规则就好办了。匹配省一级的还好,再细就有点得不偿失了。
cheese
2019-01-08 13:39:43 +08:00
已经 star,期待自定义生成功能
Everyxin
2019-01-08 13:45:20 +08:00
可以可以,听说写正则的都是大牛,有心了
fuxkcsdn
2019-01-08 14:00:07 +08:00
@vincentxue

/^(13\d|15[0-35-9]|18\d|17[0135-8]|19[89]|166|14[5-9])\d{8}$/m

我稍微优化了下,速度就比你的快了

Run the first round of testing.
461.7280960083 - 9383624
436.35296821594 - 7653307

Run the second round of testing.
468.19996833801 - 9383624
445.95909118652 - 7653307

Run the third round of testing.
465.19207954407 - 9383624
436.08498573303 - 7653307

Done.


i3-6100 8G DDR4 2133
PHP 7.0.30-0+deb9u1 (cli) (built: Jun 14 2018 13:50:25) ( NTS )
windows 10 企业版,docker desktop 2.0.0.0-win81(29211)

p.s. 匹配数暂时没优化,但我 感觉 即使匹配数一致,regex101 上那种写法也能比你的写法快(这几天我试试)
fuxkcsdn
2019-01-08 14:04:30 +08:00
@vincentxue
之所以我的运行速度能那么快主要是我把 preg_match_all 的第三个参数 $matches 去掉了

$count = preg_match_all($pattern, $data)
vincentxue
2019-01-08 14:32:04 +08:00
@cheese 谢谢,自定义这是个大坑🤣🤣,没那么快。
@Everyxin 不敢当,谢谢。
@fuxkcsdn 谢谢测试和回复,你有空可以改成匹配所有试试,效率高我可以学习改进一下的,我发到这的意图之一本来就是来讨教的😃。
fy
2019-01-10 12:01:54 +08:00
楼主哇 JS 用不了 很僵硬啊
vincentxue
2019-01-10 13:49:18 +08:00
@fy 哈哈,我知道,部分用了条件子组的正则表达式在 JavaScript 里不能用,因为 JavaScript 不支持条件子组,所以我特地标明了是 PCRE。我现在去把不兼容 JavaScript 的尝试做一下兼容,搞好通知你。
fy
2019-01-10 13:57:15 +08:00
@vincentxue #51 多谢。前端校验还是更加常见些
vincentxue
2019-01-10 20:35:52 +08:00
@fy 已经更新了,抽空去看看符不符合要求。
@fuxkcsdn 现在我为了兼容 JS 去掉了那些条件判断,用你主张的这种写法,速度大概有 6%的提升。
fy
2019-01-10 23:32:10 +08:00
@vincentxue #53 大佬真是神速,非常感谢!
fuxkcsdn
2019-01-11 11:49:13 +08:00
akatquas
2019-01-12 18:15:09 +08:00
0086 开头的电话怎么破呢?
vincentxue
2019-01-12 19:40:52 +08:00
@akatquas

拿匹配所有上网卡的正则举例:

原来的正则:^(?:\+?86)?14[579]\d{8}$

1. 允许以 “+86 ”,“ 0086 ” 以及 “ 86 ” 开头:

^(?:(?:\+?|00)86)?14[579]\d{8}$

2. 只允许以 “ 0086 ” 开头:

^(?:0086)?14[579]\d{8}$

解释:

^ # 锚点, 匹配字符串开始位置.
(?: # 非捕获组开始.
\+ # 匹配字符 "+".
? # 量词, 表示匹配 0 次 或 1 次. (作用于字符 "+")
86 # 匹配字符 "86".
) # 非捕获组结束
? # 量词, 表示匹配 0 次 或 1 次. (作用于捕获组)
14 # 匹配字符 "14".
[579] # 匹配集合中任意字符一次, 也就是 5 或 7 或 9 一次.
\d # 匹配数字, 可以视为 [0-9], 但不绝对等于.
{8} # 重复 8 次. (作用于 \d)
$ # 锚点, 匹配字符串结束位置.

连起来说,满足这个正则表达式的字符串的格式必须是:

允许以 “+86 ” 或者 “ 86 ” 开头,号段必须是 145 / 147 / 149,最后跟着 8 位数字。

这个正则里面 (?:\+?86)? 这一段是用来匹配国家码的,意思是匹配 “+86 ” 或者 “ 86 ”,如果需要再加一个可匹配项 “ 00 ”,那你改成 (?:(?:\+?|00)86)? 就可以了,这个正则匹配 “+86 ”,“ 0086 ” 以及 “ 86 ” 这三种情况。如果你只需要匹配 “ 0086 ”,那就直接改成 (?:0086)? 就可以了。
lhjl1314
2019-01-12 20:58:26 +08:00
@vincentxue 前缀可不止+86 或者 0086 哦,还有很多的,另外很多号段也缺少,你的数据虽然不是很准,但也八九不离十了
vincentxue
2019-01-12 21:53:23 +08:00
@lhjl1314

国家码的话,这个库本来是匹配手机号码的,没有做国家码匹配,后来有人有需求,我就加上了可选的国家码匹配,遵循的是目前国际通用的 E.164 格式,即 [+] [country code] [phone number] 。而且我这一个库也不可能兼容所有人的需求啊,不符合你的需求可以适当做一下修改的嘛。

另外缺少什么号段能方便说一下吗?我可以加上。
lhjl1314
2019-01-12 22:31:58 +08:00
@vincentxue 部分业务会产生特殊前缀,不过一般不会涉及到;
号段方面,例如 1349 也是卫星电话,中国移动 147、172、184 部分号段、1064[78]也是分配给物联网的等
对于普通使用者来说,楼主已经算做的很详细的了

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/524624

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX