跳到主内容
d.devtul.fun
EN
正则 · 电话

电话号码正则

一份能直接抄走的电话号码校验正则,外加一个老实话:绝大多数情况下,你根本不该用正则去校验电话号码。

正则模式

^\+[1-9]\d{1,14}$

匹配 E.164 国际格式:一个加号、一个不以 0 开头的国家码,后面最多跟 14 位数字。不允许空格、括号或连字符。

在线测试这个正则

在下面直接改文本 —— 匹配在本机完成,不会上传任何内容。

其它写法

宽松国际格式(允许分隔符)
^\+[1-9]\d{1,14}(?:[ \-.]?\d)*$

容忍数字之间的空格、连字符和点号,对人输错更宽容;但它仍然分不清"长得对"和"真的对"。

北美 NANP 严格版
^\(?([2-9]\d{2})\)?[-. ]?([2-9]\d{2})[-. ]?(\d{4})$

精确约束美国、加拿大及加勒比地区的号码。出了 NANP 范围就完全失效——而这正是陷阱:每个地区都要单独一套规则。

唯一值得抄走的那条正则

如果你只想挡掉明显的垃圾输入,并且愿意接受"长得对"不等于"真的对",那就用这条:

^\+[1-9]\d{1,14}$

它实现的是 E.164 标准,也就是每家运营商和短信网关最终存储号码的格式。长成这样的号码,可以直接丢给短信接口或电话 API,不用再做二次转换。下面要讲的,是为什么这只是整件事里最简单的那一成。

这条 E.164 正则到底匹配了什么

从左到右拆开看:

  • ^\+ —— 字符串必须以一个加号开头。这个加号就是国际前缀,在 E.164 里没有商量余地。
  • [1-9] —— 加号后面的第一位是国家码,而没有任何国家的国家码以 0 开头。前导零属于各国自己的拨号规则,不属于 E.164。
  • \d{1,14} —— 后面再跟 1 到 14 位数字,包含地区码和用户号。ITU 规定整个号码(含国家码)最长 15 位。
  • $ —— 到此为止,不允许再有别的字符,没有空格、连字符、括号。

所以 +14155552671 通过,而 +0123456789 正确地被拒,因为国家码不能以 0 开头。

为什么各国格式永远凑不到一块

只要离开 E.164,世界就碎了。同样的七位数字,换个国度意思完全不一样:

  • 美国(NANP):号码结构是三位区号、三位交换局号、四位用户号。区号和交换局号的首位都必须是 2–9,212、415、312 是真实存在的,而 012、114 不是。
  • 英国:区号长度不固定(伦敦是 20,很多城镇用 4 或 5 位),手机号以 07 开头。用一条固定宽度的正则在英国基本会写成一坨没人看得懂的东西。
  • 中国:手机号恰好 11 位,以 1 开头、后面接 3–9;座机则内嵌长度不一的区号。13912345678 是手机号,01012345678 是北京座机。

这些规则没有一条是稳定的。运营商会回收、重新分配号段,新号段不断开放,国家码也会变。今天写死在你代码里的正则,十八个月后就悄悄错了。

一份精确的北美(NANP)正则

如果你的产品只服务美国、加拿大和少数加勒比国家,NANP 这条规则值得记住:

^\(?([2-9]\d{2})\)?[-. ]?([2-9]\d{2})[-. ]?(\d{4})$

最关键的一点是区号和交换局号开头都是 [2-9]。NANP 禁止用 0 和 1 作为这两段的首位——0 留给总机,1 留作长途接入。可选的 \(? 和 \)? 让用户既可以写 (415) 555-2671,也可以写 415-555-2671。这条正则既正确又紧凑,而这也正是它的危险所在:它只对一个地区正确,放到别处就彻底翻车。

为什么你不该用正则校验电话号码

这是本页的核心。正则检查的是"形状",不是"合法性"。Google 的 libphonenumber 库之所以存在,正是因为形状检查既简单又基本没用。这个库带着每个国家完整且频繁更新的元数据:哪些号码长度是合法的、哪些号段被分配了、怎么把本地格式归一化成 E.164、怎么识别明显的手误。当某个国家重新编号时,你跑一次库升级就行,而不是发一个正则补丁、还指望没人发现。

用库来解析和格式化。如果你确实需要确认一个号码是活的,就发一条短信或打个语音电话做一次性验证——那才是唯一有意义的检查。

正则依然会搞砸的地方

  • 把国家码写死:写成 ^\+1\d{10}$ 等于把自己锁死在美国,悄悄拒绝所有海外用户。不带国家码假设、只用 E.164 反而更安全。
  • 禁止分隔符:用户会输入 +44 20 8366 1177 这种带空格的写法。测试前先去掉非数字字符,或者在正则里放行分隔符,否则你会拒掉合法输入。
  • 把分机号吃掉:企业号码常带 x1234 或 ext. 1234。过窄的正则会把这部分丢掉,电话永远到不了该找的人。
  • 轻信结果:形状完美的号码依然可能根本没被分配。正则永远无从得知这一点。

格式校验 vs 真实存在

把这两个问题分开,因为混为一谈就会出 bug:

  • "这串字符长得像个电话号码吗?" —— 正则或 libphonenumber 都能回答。成本低,适合在提交表单时挡掉手误。
  • "这个号码属于一个真实、能联系上的人吗?" —— 只有验证消息能回答。任何静态模式都做不到,因为"是否被分配"是运营商掌握的实时事实。

把精力花在第二个问题上,用验证消息来确认;形状检查只当作第一道门槛就好。

真例子:放行与拒绝

输入结果原因
+14155552671放行合法 E.164,美国号码,加号后 11 位
+442083661177放行合法 E.164,英国号码
+8613912345678放行合法 E.164,中国手机号
+0123456789拒绝国家码不能以 0 开头
14155552671拒绝缺少必需的加号前缀
+1415555拒绝位数太少,不是任何真实 NANP 号码

该用什么来代替

把这个问题交给有人维护的库。Python 里这样写:

from phonenumbers import parse, is_valid_number

try:
    n = parse("+14155552671", None)
    if is_valid_number(n):
        print("看起来是真的")
except Exception:
    print("连号码都不是")

JavaScript 里用 libphonenumber-js 包,思路一样。先解析,再校验,最后用消息验证。你的正则只是块门垫,不是保安。

常见问题

用正则校验电话号码够吗?

不够。正则只检查形状。校验要用 libphonenumber,确认号码真实存在得发短信验证码。

E.164 的正确正则是什么?

用 ^\+[1-9]\d{1,14}$,一个加号、非零开头的国家码,后面最多 14 位数字,不含分隔符。

为什么我的电话正则会拒掉合法的国外号码?

多半是你把国家码写死了,或者禁掉了空格和连字符。先去掉非数字字符,再按 E.164 测。

正则能判断一个号码是否在使用吗?

永远不能。只有发验证消息才能证明号码能联系到真实的人。

其它正则速查

打开完整的正则测试器