跳到主内容
d.devtul.fun
EN
正则 · 邮箱

邮箱正则

一个能直接上手的邮箱校验正则,外加你根本不该自己手写邮箱校验的理由。

正则模式

^[^\s@]+@[^\s@]+\.[^\s@]{2,}$

它接受一个本地名、一个 @、一个域名,以及一个点后面跟着两位以上的顶级域。它能挡掉空格和明显畸形的输入,但不假装自己覆盖了所有情况。

在线测试这个正则

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

其它写法

宽松提取版
^[^@\s]+@[^@\s]+\.[^@\s]+$

读起来更省心,也能挡掉大部分垃圾,但它允许一位长度的顶级域,并且点可以出现在任意位置。

贴近 RFC 的 ASCII 版
^[A-Za-z0-9._%+\-]+@[A-Za-z0-9.\-]+\.[A-Za-z]{2,}$

把本地名限定在常见 ASCII 字符内,并要求字母组成的顶级域。它依然会拒绝合法的国际化邮箱。

日常用的邮箱正则

大多数应用其实并不需要"完美"的邮箱校验器。它们要的只是一个能在注册时拦住手滑输错、挡掉空字符串、挡掉多余空格、挡掉漏写 @ 的符号组合。当同事在代码评审里问"来个邮箱正则"的时候,我一般给下面这一行:

^[^\s@]+@[^\s@]+\.[^\s@]{2,}$

它很短,几乎能原样贴进任何正则引擎(PCRE、Python、JavaScript、Go 都吃得下),而且它没有假装自己是一套标准。把它当成一道门,而不是一纸判决书。

这个正则到底匹配了什么

从左到右读,每一段都有存在的理由:

  • ^ 把匹配锚定在字符串开头,这样合法地址才不会藏在长句子中间蒙混过关。
  • [^\s@]+ 是本地名。它允许一个或多个既不是空白也不是 @ 的字符,于是 user、name.surname+tag、dev.team 都过得去。
  • @ 是那个分隔符,而且必须恰好一个。
  • [^\s@]+ 是域名,同样拒绝空白和第二个 @。
  • \. 匹配一个真正的小数点。注意反斜杠:没转义的点会匹配任意字符,于是 a@b_c 也会被放进来。
  • [^\s@]{2,} 是顶级域,强制两位及以上、且非空白非 @ 的字符。正是它把 [email protected] 这种一位顶级域挡在门外。
  • $ 锚定结尾,所以尾随的垃圾会失败。

结果就是:它接受 [email protected] 和 [email protected],拒绝 @nope.com、no-at-sign.com 和 a@b c。

RFC 5322 怎么把"合法"这件事搅黄了

如果你去翻"正确"的邮箱正则,迟早会撞上 RFC 5322。它允许的一些写法,几乎没有任何注册表单应该接受,而一份完全合规的正则长达几百字符,实战里还是错的。随便举几个:

  • 引号包裹的本地名里可以有空格、可以有 @、可以有大部分标点:"john doe"@example.com 甚至 "a\b@c"@example.com 都合法。
  • 圆括号注释几乎能插在任何地方:user(comment)@example.com。
  • 域名可以是一个方括号里的 IP 字面量:user@[192.168.1.1]。
  • 国际化邮箱在本地名里用 UTF-8,纯 ASCII 的正则根本描述不了。

写一条把这些全放行的正则,技术上做得到,意义却没有。你会拒绝掉用户真实在用的地址,同时又放行你的邮件服务器会直接弹回的地址。

为什么正则永远给不出正确答案

核心论点很简单:正则只能查"形状",查不了"可达性"。一个满足地球上所有规则的形状,如果没人看那个收件箱,就一文不值。真正算数的校验只有一种——发一封带确认链接的邮件,然后等对方点开。这一步同时证明了三件事,而这是任何正则都做不到的:地址是真的、这个人控制着它、而且他愿意收你的信。

所以正确的架构很无聊:用一条宽松正则在表单层拦掉明显手滑,把过关的存下来,然后发验证邮件。如果点不开,账号就一直是不活跃状态。没有哪条正则能替掉这一步。

把真实地址写残的常见错误

下面这些是我在生产代码里见得最多的坑:

坏习惯它害了谁改法
在本地名里禁掉 +Gmail 用户失去 name+shop 标签,这是人家依赖的功能放行 + 和多数 ASCII 标点
拒绝子域名[email protected] 合法且常见用 @ 后接一个或多个点分隔标签
把 . 写成没转义的 .没转义的点匹配任意字符转义它:\.
写死顶级域白名单新顶级域不断冒出来,你会过时改成要求长度 {2,}

合法与非法,一眼对照

用下面这张表快速测一下你正在考虑的正则。一条靠谱的实用正则应当接受上半组、拒绝下半组。

输入期望说明
[email protected]接受最正常的样子
[email protected]接受加号标签加子域名
@nope.com拒绝本地名为空
spaces [email protected]拒绝空格不允许
[email protected]拒绝一位顶级域

比正则有用的:语言内置校验

每个正经平台都自带邮箱检查器,而且几乎都比你手写的强:

  • HTML5 直接给了你 <input type="email">,免费套用浏览器自己的规则和原生界面。
  • Python 有 email.utils.parseaddr 以及 email 头解析器。
  • JavaScript 没有内置校验器,但封装了 RFC 的库一行就能装上。
  • 多数后端框架(Django、Rails、Laravel)在模型层就帮你校验了邮箱。

该靠它们就靠它们,自定义正则保持宽松,把严格版本留给日志解析这种只需要快速启发式的地方。

更严格的变体,以及什么时候用它

如果你必须拒绝 ASCII 之外的任何东西,下面这个变体把本地名锁进一个安全字符集,并要求字母顶级域:

^[A-Za-z0-9._%+\-]+@[A-Za-z0-9.\-]+\.[A-Za-z]{2,}$

拿它对付那些一碰到引号或 Unicode 就崩的遗留系统。但别拿它当"地址可用"的证明,也别拿它去挡那些有充分权利使用非 ASCII 邮箱的国际用户。

怎么测你的正则

把你候选的那条连同上面的正常样例和陷阱样例一起丢进实时测试器。重点盯两种失败:一种是放行了 [email protected](顶级域太短),一种是拒绝了 [email protected](禁了加号)。能扛住这两下的,做表单层检查就够用了。记住收尾这条规矩:正则管形状,确认邮件管真相。

常见问题

最好的邮箱正则是什么?

没有最好的。用宽松正则拦手滑,再用确认邮件做最终验证。

正则够不够做邮箱校验?

不够。正则只查形状;可达性只有等用户点开验证邮件里的链接才算数。

为什么我的正则会拒绝加号地址?

多半是你把加号排除了。Gmail 用户靠 name+tag 这种写法,本地名里应当放行它。

顶级域要写白名单吗?

不要。新顶级域一直在增加,改成要求长度两位以上,而不是写死列表。

其它正则速查

打开完整的正则测试器