V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
vincentxue
V2EX  ›  分享创造

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

  •  8
     
  •   vincentxue ·
    vincentsit · 2019-01-07 14:16:17 +08:00 · 7852 次点击
    这是一个创建于 2152 天前的主题,其中的信息可能已经有所发展或是发生改变。



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

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

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

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

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

    需要说明的是因为考虑到方便使用的原因,有些正则其实并不是传统意义上极致优化的,可以根据自身需要去修改。例如不需要匹配国家码,可以去掉匹配国家码的那一段。但除非匹配量非常巨大,否则这个性能差别是可以忽略的。我在我机器上用 PHP 7 测了一下匹配一百多万行的手机号码,性能差距还是能接受的。
    61 条回复    2019-01-12 22:58:34 +08:00
    vincentxue
        1
    vincentxue  
    OP
       2019-01-07 14:45:59 +08:00 via iPhone   ❤️ 1
    点不点 Star 无所谓,但建议使用或需要关注的朋友们做一下 Release Only Watch,以后每次更新都会 Release 一个版本来触发通知,这样大家就能及时更新。
    dezhou9
        2
    dezhou9  
       2019-01-07 14:48:35 +08:00 via Android   ❤️ 1
    这个用来识别非手机号可以的,star
    dezhou9
        3
    dezhou9  
       2019-01-07 14:49:59 +08:00 via Android   ❤️ 1
    其实手机号前面的+也可以是 00,这个谷歌自己补一下 k
    STGing
        4
    STGing  
       2019-01-07 14:50:30 +08:00   ❤️ 1
    已 star,好东西!作者费心了。
    vincentxue
        5
    vincentxue  
    OP
       2019-01-07 14:58:37 +08:00 via iPhone
    @dezhou9 这个如果有需求可以加一个条件,我是看大家都是用+所以我也从众了。
    @STGing 谢谢哈!
    Lax
        6
    Lax  
       2019-01-07 14:59:29 +08:00   ❤️ 1
    早上看到个消息要启用 165 号段
    lion9527
        7
    lion9527  
       2019-01-07 15:03:17 +08:00   ❤️ 1
    我个人感觉如果有短信验证码,则不必过于拟合。
    直接泛化可能工作更轻松,比如 1 开头,11 位数即可。
    lizhenda
        8
    lizhenda  
       2019-01-07 15:11:16 +08:00   ❤️ 1
    不错不错,有心了!
    yuewolf
        9
    yuewolf  
       2019-01-07 15:12:02 +08:00 via Android   ❤️ 1
    素质二连,楼主有心了。
    deathscythe
        10
    deathscythe  
       2019-01-07 16:03:02 +08:00   ❤️ 1
    哇卡卡, star 了, 不错的
    dezhou9
        11
    dezhou9  
       2019-01-07 16:15:33 +08:00 via Android   ❤️ 2
    @lion9527 规则上细化不算拟合。作者未来支持新号段发现,才有拟合概念
    justfindu
        12
    justfindu  
       2019-01-07 16:30:55 +08:00   ❤️ 1
    好久前就用了 好用!
    ResidualWind
        13
    ResidualWind  
       2019-01-07 16:35:25 +08:00   ❤️ 1
    已 star, 感谢作者
    kingsleydon
        14
    kingsleydon  
       2019-01-07 16:35:46 +08:00   ❤️ 1
    试用了一下,已 star ~
    usapla
        15
    usapla  
       2019-01-07 16:48:46 +08:00   ❤️ 1
    该说不说,楼主真把我们当亲弟弟了
    GuryYu
        16
    GuryYu  
       2019-01-07 16:49:08 +08:00   ❤️ 1
    楼主这个图是用什么工具生成的呀
    Removable
        17
    Removable  
       2019-01-07 17:01:14 +08:00   ❤️ 1
    咦,不知道什么时候已经 star 了
    ShundL
        18
    ShundL  
       2019-01-07 17:06:59 +08:00   ❤️ 1
    这是高手,已 star
    fmumu
        19
    fmumu  
       2019-01-07 17:09:43 +08:00   ❤️ 1
    可以搞个动态生成器,每个人的需求都不一样的,根据选择的条件如:电信 /联通+iot 来生成正则表达式
    mwiker
        20
    mwiker  
       2019-01-07 17:10:55 +08:00   ❤️ 1
    已 star,可以增加多个开发语言的版本。
    Paff
        21
    Paff  
       2019-01-07 17:14:04 +08:00   ❤️ 1
    star 了,好东西
    phpcxy
        22
    phpcxy  
       2019-01-07 17:20:50 +08:00   ❤️ 2
    我都是 1 开头的 11 位数字
    vincentxue
        23
    vincentxue  
    OP
       2019-01-07 17:21:46 +08:00
    哇谢谢大家哈,好多人点 Star,谢谢,但是记得 Watch 一下才能及时得到更新啊。


    @Lax 谢谢提醒,我稍后确认一下资料就加上。

    @lion9527 嗯可能验证码可以不用那么严格,但是各家有各家的需求,有些可能专门匹配手机号码,可能就要求精确一些。

    @usapla 啥啥啥?

    @GuryYu 就是正则链接到的那个网站啊。https://www.debuggex.com/ ,输入正则就会显示了,然后需要分享点上方的 Share 就可以。

    @fmumu 这个又得挖坑了,我先把各语言的库填一下,填完就搞一个。

    @mwiker 正在开发中了,不会很久。
    Heanes
        24
    Heanes  
       2019-01-07 17:26:51 +08:00   ❤️ 1
    这个比较可敬,有毅力有恒心
    blue0125
        25
    blue0125  
       2019-01-07 17:28:53 +08:00 via Android   ❤️ 2
    支持带号转网了😂
    vincentxue
        26
    vincentxue  
    OP
       2019-01-07 17:34:56 +08:00
    @Heanes 还有很多朋友督促和帮助。
    @blue0125 这个正则层面就没有办法了啊,文档也写明了。
    iVeego
        27
    iVeego  
       2019-01-07 17:35:34 +08:00   ❤️ 2
    @livid 我记得之前你问过有无类似的服务。
    fuxkcsdn
        28
    fuxkcsdn  
       2019-01-07 17:40:02 +08:00   ❤️ 2
    楼主有测试数据吗?
    放到 regex101 上测试突然想到这个网站有个 regex library,搜索了下还真有

    https://regex101.com/r/dnOlNH/1

    虽然上面这种表达式不好看,但效率上比楼主写的高

    p.s. regex101 那个表达式不完整,配合楼主的图或者下面的 wiki 改造下就行了
    https://zh.wikipedia.org/wiki/%E4%B8%AD%E5%9B%BD%E5%86%85%E5%9C%B0%E7%A7%BB%E5%8A%A8%E7%BB%88%E7%AB%AF%E9%80%9A%E8%AE%AF%E5%8F%B7%E6%AE%B5
    v2byy
        29
    v2byy  
       2019-01-07 17:40:23 +08:00   ❤️ 1
    已关注
    beiyu
        30
    beiyu  
       2019-01-07 17:57:15 +08:00   ❤️ 1
    难得一见的好东西 mark 了
    struggletoday
        31
    struggletoday  
       2019-01-07 18:00:46 +08:00   ❤️ 1
    star
    NonClockworkChen
        32
    NonClockworkChen  
       2019-01-07 18:07:52 +08:00   ❤️ 1
    这个轮子会把成千上万的轮子扼杀,star。
    0312birdzhang
        33
    0312birdzhang  
       2019-01-07 18:51:43 +08:00   ❤️ 1
    star star,好东西
    vincentxue
        34
    vincentxue  
    OP
       2019-01-07 20:15:01 +08:00
    @fuxkcsdn

    Hi,你点每个正则的链接,进去之后看最下面的匹配项即可。

    你这个正则我看了,也测试了一下,我这个要快 30%左右。

    ➜ Code sh run-benchmark.sh

    Run the first round of testing.

    2815.1209354401 - 9383624
    3864.3181324005 - 7653307

    Run the second round of testing.

    2795.1259613037 - 9383624
    3878.3061504364 - 7653307

    Run the third round of testing.

    2758.6340904236 - 9383624
    3866.0190105438 - 7653307

    Done.

    主要原因我在兼顾方便的同时我还是尽量做了优化的。这里就不细讲了。那个正则里面有很多可以优化项,例如尽量减少分支条件,左侧优先,起始结尾描点优化等等。举个最简单的例子,他那个里面是 13、14、15 等等这样排序匹配的,我这里面 14、16 等使用频率低的号段我都放到最后面去了,这样能减少不必要的运算。

    https://drive.google.com/file/d/1RkkRb7TSLYTdXoQaJmT8IVaRM-6UaPB1/view?usp=sharing 这是我刚才做的简单对比测试,包含 1000 万随机生成的手机号码。
    cutlove
        35
    cutlove  
       2019-01-07 20:47:02 +08:00   ❤️ 1
    怎么 @ 站长,之前换手机号失败了,198 的号段
    jiqing
        36
    jiqing  
       2019-01-07 20:50:54 +08:00   ❤️ 1
    这种好项目 star 又不会掉块肉
    vincentxue
        37
    vincentxue  
    OP
       2019-01-07 21:31:57 +08:00
    @cutlove 就 @Livid 就可以了。
    whypool
        38
    whypool  
       2019-01-07 22:01:55 +08:00
    1 开头 11 位数还要啥正则?
    vincentxue
        39
    vincentxue  
    OP
       2019-01-07 22:15:36 +08:00
    @Lax 165 号段已经加上了,感谢提醒。
    vincentxue
        40
    vincentxue  
    OP
       2019-01-07 22:18:14 +08:00
    @whypool 总有些人需要精确匹配的。
    xxxiong
        41
    xxxiong  
       2019-01-08 09:34:01 +08:00 via iPhone   ❤️ 1
    支持,也是有心了
    qingxiangcool
        42
    qingxiangcool  
       2019-01-08 09:35:02 +08:00   ❤️ 1
    已 star
    RyougiShiki
        43
    RyougiShiki  
       2019-01-08 10:15:06 +08:00   ❤️ 1
    原来公司业务需求匹配某个省的移动号,写正则挺费心的。
    vincentxue
        44
    vincentxue  
    OP
       2019-01-08 10:53:37 +08:00 via iPhone
    @RyougiShiki 你这个需求用数据库可能更合适啊,正则感觉不是很适合啊。不过主要还是要有规则,有了规则就好办了。匹配省一级的还好,再细就有点得不偿失了。
    cheese
        45
    cheese  
       2019-01-08 13:39:43 +08:00   ❤️ 1
    已经 star,期待自定义生成功能
    Everyxin
        46
    Everyxin  
       2019-01-08 13:45:20 +08:00   ❤️ 1
    可以可以,听说写正则的都是大牛,有心了
    fuxkcsdn
        47
    fuxkcsdn  
       2019-01-08 14:00:07 +08:00   ❤️ 1
    @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
        48
    fuxkcsdn  
       2019-01-08 14:04:30 +08:00   ❤️ 1
    @vincentxue
    之所以我的运行速度能那么快主要是我把 preg_match_all 的第三个参数 $matches 去掉了

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

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

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

    我的资料依据是我文档上附上的那个维基百科,你说的 1349 也是卫星电话资料上确实也是这样显示的,我是根据运营商来分类的,没有根据网络来分类,所以就随维基归到电信里面了。其他的 172 我找不到公开资料,“ 184 部分号段、1064[78]也是分配给物联网的等” 184 没查到,1064 查到确实是也分配给了物联网。

    感谢批评指正,确实还没有做到绝对精准,我会去查查工信部的相关文档,再提升一下准确性。如果你有相关资料方便共享的话也可以分享一下。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2492 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 16:07 · PVG 00:07 · LAX 08:07 · JFK 11:07
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.