如何寻找或验证电子邮件地址如何寻找或验证电子邮件地址如何寻找或验证电子邮件地址如何寻找或验证电子邮件地址
  • 文章
  • 正则表达式
    • 工具
  • 登录
找到的结果: {phrase} (显示: {results_count} 共: {results_count_total})
显示: {results_count} 共: {results_count_total}

加载更多搜索结果...

搜索范围
模糊匹配
搜索标题
搜索内容
发表 admin at 2024年3月5日
类别
  • 正则表达式
标签
如何寻找或验证电子邮件地址
  • 简
  • 繁
  • En
关于正则表达式 » 正则表达式范例 » 如何寻找或验证电子邮件地址

范例
正则表达式范例
数字范围
浮点数
电子邮件地址
IP 地址
有效日期
数字日期转文本
信用卡号码
比对完整行
删除重复行
编程
两个相近的字词
陷阱
灾难性的回溯
过多重复
拒绝服务
让所有内容都可选
重复捕获组
Unicode 和 8 比特混合
更多内容
简介
正则表达式快速入门
正则表达式教程
替换字符串教程
应用程序和语言
正则表达式范例
正则表达式参考
替换字符串参考

如何寻找或验证电子邮件地址

我收到最多回馈的正则表达式,更别提「错误」回报,就是您会在本网站的首页上找到的这个:\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b。我声称这个正则表达式可以比对任何电子邮件地址。我收到的回馈大多反驳这个说法,并指出这个正则表达式无法比对的电子邮件地址。通常,「错误」回报也会包含建议,让这个正则表达式变得「完美」。

正如我在下方说明的,我的说法只有在我接受对有效电子邮件地址的定义,以及什么不是有效电子邮件地址的定义时,才成立。如果您想使用不同的定义,您必须调整正则表达式。比对有效的电子邮件地址是一个完美的范例,说明 (1) 在撰写正则表达式之前,您必须确切知道您要比对什么,以及什么不要比对;(2) 精确性和实用性之间通常会有取舍。

上述正则表达式的优点在于它可以比对当今使用中的 99% 电子邮件地址。它比对的所有电子邮件地址都可以由 99% 的所有电子邮件软件处理。如果您正在寻找快速解决方案,您只需要阅读下一段落。如果您想了解所有取舍,并从中选择大量替代方案,请继续阅读。

如果您想使用上述正则表达式,您需要了解两件事。首先,长正则表达式会让段落难以正确格式化。因此,我在三个字符类别中都没有包含 a-z。此正则表达式旨在与您的正则表达式引擎的「不分大小写」选项一起使用。(您会讶异我收到多少关于此的「错误」报告。)其次,上述正则表达式以 字符边界 为界定,这使其适用于从文件或较大区块文本中萃取电子邮件地址。如果您想检查用户是否输入有效的电子邮件地址,请将字符边界替换为 字符串开头和字符串结尾锚点,如下所示:^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$。

前一段落也适用于以下所有范例。您可能需要将字符边界变更为字符串开头/结尾锚点,或反之亦然。而且您必须打开不分大小写比对选项。

验证电子邮件地址的取舍

在 ICANN 让任何资金雄厚的公司都能创建自己的顶层网域之前,最长的顶层网域是鲜少使用的 .museum 和 .travel,长度为 6 个字母。最常见的顶层网域长度为 2 个字母,用于特定国家的网域,以及 3 或 4 个字母,用于一般用途的网域,例如 .com 和 .info。您会在各种正则表达式教程和参考中找到许多用于验证电子邮件地址的正则表达式,这些正则表达式仍然假设顶层网域相当短。较旧版本的此正则表达式教程在引言中提到 \b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b 作为电子邮件地址的正则表达式。这个正则表达式和本页顶端的正则表达式只有一个小差异。正则表达式结尾的 4 将顶层网域限制为 4 个字符。如果您使用这个正则表达式和锚点来验证订单表单中输入的电子邮件地址,fabio@domain.solutions 就必须到其他地方购物。是的,.solutions TLD 存在,而我在撰写本文时,domain.solutions 每年售价 16.88 美元。

如果您想对顶层网域比 [A-Z]{2,} 更加严格,^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,63}$ 是您实际上能达到的极限。网域名称的每个部分长度不得超过 63 个字符。没有单一数字的顶层网域,也没有任何顶层网域包含数字。看起来 ICANN 也不会核准此类网域。

电子邮件地址可以位于子网域的服务器上,如 john@server.company.domain.com。由于我在 @ 符号后的字符类别中包含一个点,因此所有上述正则表达式都符合此电子邮件地址。但上述正则表达式也符合 john@domain...com,由于连续的点,因此无效。你可以通过在上述任何正则表达式中将 [A-Z0-9.-]+\. 替换为 (?:[A-Z0-9-]+\.)+ 来排除此类符合项。我从字符类别中移除点,而重复使用字符类别和后面的文本点。例如,^[A-Z0-9._%+-]+@(?:[A-Z0-9-]+\.)+[A-Z]{2,}$ 符合 john@server.company.domain.com,但不符合 john@domain...com。

如果你想避免系统因任意大的输入而中断,你可以将无限 量词 替换为有限量词。^[A-Z0-9._%+-]{1,64}@(?:[A-Z0-9-]{1,63}\.){1,125}[A-Z]{2,63}$ 考量到本地端 (在 @ 之前) 限制为 64 个字符,而网域名称的每个部分限制为 63 个字符。子网域的数量没有直接限制。但 SMTP 可以处理的电子邮件地址最大长度为 254 个字符。因此,如果本地端为单一字符、顶层网域为两个字符,且子网域为单一字符,则子网域的最大数量为 125。

以前的正则表达式实际上并没有将电子邮件地址限制为 254 个字符。如果每个部分都达到其最大长度,则正则表达式可以匹配长度不超过8129个字符的字符串。您可以通过将允许的子域数量从125个减少到更现实的值(如8个)来减少这种情况。我从未见过包含超过 4 个子域的电子邮件地址。如果要强制运行 254 个字符的限制,最好的解决方案是在使用正则表达式之前检查输入字符串的长度。虽然这需要几行过程代码,但检查字符串的长度几乎是瞬间的。如果只能使用正则表达式,则可以使用 ^[A-Z0-9@._%+-]{6,254}$ 作为第一次传递,以确保字符串不包含无效字符,并且不会太短或太长。如果你需要用一个正则表达式做所有事情,你就需要一个支持前瞻的正则表达式风格。正则表达式 ^(?=[A-Z0-9@._%+-]{6,254}$)[A-Z0-9._%+-]{1,64}@(?:[A-Z0-9-]{1,63}\.){1,8}[A-Z]{2,63}$ 使用 lookahead 首先检查字符串是否不包含无效字符,以及是否太短或太长。当前瞻成功时,正则表达式的其余部分将第二次遍历字符串,以检查 @ 符号和点的位置是否正确。

这些正则表达式都允许在本地端部分的任何地方使用字符 ._%+-。你可以使用 ^[A-Z0-9][A-Z0-9._%+-]{0,63} 来强制本地端部分以字母开头,而不是使用 ^[A-Z0-9._%+-]{1,64} 作为本地端部分:^[A-Z0-9][A-Z0-9._%+-]{0,63}@(?:[A-Z0-9-]{1,63}\.){1,125}[A-Z]{2,63}$。在使用向前预查来检查地址的整体长度时,可以在向前预查中检查第一个字符。在检查本地端部分的长度时,我们不需要重复初始字符检查。这个正则表达式太长了,不适合页面的宽度,所以让我们打开自由间距模式

^(?=[A-Z0-9][A-Z0-9@._%+-]{5,253}$)
[A-Z0-9._%+-]{1,64}@(?:[A-Z0-9-]{1,63}\.){1,8}[A-Z]{2,63}$

网域名称可以包含连字号。但它们不能以连字号开头或结尾。[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])? 匹配长度在 1 到 63 个字符之间且以字母或数字开头和结尾的网域名称。非捕获组使网域名称中间和最后一个字母或数字作为一个整体是可选的,以确保我们允许单字符网域名称,同时确保包含两个或多个字符的网域名称不会以连字号结尾。整体正则表达式开始变得相当复杂

^[A-Z0-9][A-Z0-9._%+-]{0,63}@
(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.){1,8}[A-Z]{2,63}$

功能变量名称不能包含连续的连字符。[A-Z0-9]+(?:-[A-Z0-9]+)* 匹配以字母或数字开头和结尾的域名,并且包含任意数量的非连续连字符。这是最有效的方法。此正则表达式不会运行任何回溯以匹配有效的功能变量名称。它匹配功能变量名称开头的所有字母和数字。如果没有连字符,则后面的可选组将立即失败。如果有连字符,则该组将匹配每个连字符,后跟所有字母和数字,直到下一个连字符或功能变量名称的末尾。当连字符必须与字母或数字配对时,我们无法强制运行最大长度,但字母和数字可以独立存在。但是,我们可以使用用于强制运行电子邮件地址总长度的前瞻技术来强制运行域名的长度,同时不允许连续的连字符:(?=[A-Z0-9-]{1,63}\。)[A-Z0-9]+(?:-[A-Z0-9]+)*.请注意,lookahead 还会检查当功能变量名称在电子邮件地址中完全限定时必须出现在域名之后的点。这很重要。如果不检查点,lookahead 将接受更长的功能变量名称。由于 lookahead 不使用它匹配的文本,因此点不包含在此正则表达式的整体匹配中。当我们将此正则表达式放入电子邮件地址的整体正则表达式中时,点将像在前面的正则表达式中一样匹配:

^[A-Z0-9][A-Z0-9._%+-]{0,63}@
(?:(?=[A-Z0-9-]{1,63}\.)[A-Z0-9]+(?:-[A-Z0-9]+)*\.){1,8}[A-Z]{2,63}$

如果我们包含前瞻来检查整体长度,我们的正则表达式会在本地端部分运行两次,在网域名称运行三次,以验证所有内容

^(?=[A-Z0-9][A-Z0-9@._%+-]{5,253}$)[A-Z0-9._%+-]{1,64}@
(?:(?=[A-Z0-9-]{1,63}\.)[A-Z0-9]+(?:-[A-Z0-9]+)*\.){1,8}[A-Z]{2,63}$

在现代的个人电脑或服务器上,当验证单一 254 字符的电子邮件地址时,这个正则表达式将运行得很好。拒绝较长的输入会更快,因为当先行断言在第一次通过时失败时,正则表达式就会失败。但我不会建议使用像这样复杂的正则表达式来通过大量文档或信件存盘来搜索电子邮件地址。你最好使用此页面顶端的简单正则表达式来快速收集看起来像电子邮件地址的所有内容。对结果进行重复数据删除,然后如果你想进一步筛选出无效的地址,请使用更严格的正则表达式。

说到回溯,此页面上的正则表达式都没有进行任何回溯来比对有效的电子邮件地址。但特别是后者,可能会对不是有效电子邮件地址的东西进行相当多的回溯。如果你的正则表达式风格支持占有量词,你可以通过让所有量词都变成占有量词来消除所有回溯。因为不需要回溯来寻找比对,所以这样做不会改变这些正则表达式比对的内容。它只允许它们在输入不是有效的电子邮件地址时更快地失败。正确处理次网域的最简单正则表达式随后变成 ^[A-Z0-9._%+-]++@(?:[A-Z0-9-]++\.)++[A-Z]{2,}+$,在每个量词后加上一个额外的 +。我们可以对我们最复杂的正则表达式运行相同的操作

^(?=[A-Z0-9][A-Z0-9@._%+-]{5,253}+$)[A-Z0-9._%+-]{1,64}+@
(?:(?=[A-Z0-9-]{1,63}+\.)[A-Z0-9]++(?:-[A-Z0-9]++)*+\.){1,8}+[A-Z]{2,63}+$

所有这些正则表达式中的一个重要取舍是,它们只允许英文本母、数字和最常用的特殊符号。主要原因是我不相信我所有的电子邮件软件都能处理其他内容。即使 John.O'Hara@domain.com 是语法上有效的电子邮件地址,但有些软件可能会误将撇号解释为分隔符号。例如,将此电子邮件地址盲目插入 SQL 查找中,最好的情况是当字符串以单引号分隔时会导致失败,最坏的情况是会让你的网站面临 SQL 注入攻击。

当然,网域名称已经可以包含非英文本元很多年了。但大多数软件仍然坚持西方程序员习惯使用的 37 个字符。支持国际化网域会打开一个问题,也就是非 ASCII 字符应该如何编码。因此,如果你使用此页面上的任何正则表达式,任何具有 @โดเมน.ไทย 地址的人都会很倒楣。

结论是,要决定使用哪个正则表达式,无论你是要比对电子邮件地址或其他定义模糊的东西,你都需要先考虑所有取舍。比对到无效的东西有多糟?不比对到有效的东西有多糟?你的正则表达式可以有多复杂?如果你必须在稍后变更正则表达式,因为它太广泛或太狭窄,那会有多昂贵?这些问题的不同答案将需要不同的正则表达式作为解决方案。我的电子邮件正则表达式可以满足我的需求,但它可能无法满足你的需求。

正则表达式不会发送电子邮件

不要过度尝试使用正则表达式来消除无效的电子邮件地址。原因在于,在尝试寄送电子邮件之前,你并不知道一个地址是否有效。而且,即使如此,这可能还不够。即使电子邮件已送达某个信箱,这并不表示有人会读取该信箱。如果你真的需要确定某个电子邮件地址是否有效,你必须寄送一封电子邮件给该地址,其中包含一个代码或链接,让收件者运行第二个验证步骤。如果你这么做,那么使用可能会拒绝有效电子邮件地址的正则表达式就没有什么意义了。

相同的原则适用于许多情况。在尝试比对有效日期时,通常使用一点算术来检查闰年会比较容易,而不是尝试在正则表达式中运行此操作。使用正则表达式来寻找潜在的比对或检查输入是否使用正确的语法,并对正则表达式传回的潜在比对运行实际验证。正则表达式是一个强大的工具,但它们远非万灵丹。

官方标准:RFC 5322

你可能想知道为什么没有「官方」的万无一失正则表达式来比对电子邮件地址。嗯,有一个官方定义,但它并非万无一失。

官方标准称为RFC 5322。它描述了有效电子邮件地址必须遵守的语法。你可以(但你不应该这么做,请继续阅读)使用以下正则表达式来实作它。RFC 5322 让网域名称部分开放给实作特定的选择,这些选择在今日的互联网上无法运作。正则表达式实作了RFC 1035中的「首选」语法,这是 RFC 5322 中的建议之一。

\A(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*
 
|  "(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]
      
|  \\[\x01-\x09\x0b\x0c\x0e-\x7f])*")
@ (?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?
  
|  \[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}
       
(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:
          
(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]
          
|  \\[\x01-\x09\x0b\x0c\x0e-\x7f])+)
     
\])\z

此正则表达式分为两部分:@ 符号之前的部分和 @ 符号之后的部分。@ 符号之前的部分有两个选项。第一个选项允许它包含一系列字母、数字和某些符号,包括一个或多个点。但是,点不能连续出现,也不能出现在电子邮件地址的开头或结尾。另一个选项要求 @ 符号之前的部分用双引号括起来,允许在引号之间使用任何 ASCII 字符串。空白字符、双引号和反斜线必须用反斜线进行转义。

@ 符号之后的部分也有两个选项。它可以是完全限定的网域名称,或者可以是方括号中的文本互联网地址。文本互联网地址可以是 IP 地址,也可以是特定网域的路由地址。

不应使用此正则表达式的原因是它过于广泛。您的应用程序可能无法处理此正则表达式允许的所有电子邮件地址。特定网域的路由地址可能包含不可打印的 ASCII 控制字符,如果您的应用程序需要显示地址,这可能会造成问题。并非所有应用程序都支持使用双引号或方括号的本地部分语法。事实上,RFC 5322 本身将使用方括号的表示法标记为过时。

如果我们省略 IP 地址、特定网域地址、使用双引号和方括号的语法,我们将获得更实用的 RFC 5322 实作。它仍然会符合当今实际使用中 99.99% 的所有电子邮件地址。

\A[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@
(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\z

这两个正则表达式都不对整体电子邮件地址、本地部分或网域名称强制长度限制。RFC 5322 没有指定任何长度限制。这些限制源于其他协定的限制,例如实际发送电子邮件的 SMTP 协定。RFC 1035 确实指出网域必须为 63 个字符或更少,但没有将其包含在其语法规范中。原因是真正的正规语言无法同时强制长度限制和禁止连续连字号。但现代正则表达式并非真正的正则表达式,因此我们可以使用前瞻检查来添加长度限制检查,就像我们之前所做的那样

\A(?=[a-z0-9@.!#$%&'*+/=?^_`{|}~-]{6,254}\z)
 
(?=[a-z0-9.!#$%&'*+/=?^_`{|}~-]{1,64}@)
 
[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*
@ (?:(?=[a-z0-9-]{1,63}\.)[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+
  
(?=[a-z0-9-]{1,63}\z)[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\z

因此,即使遵循官方标准,仍然需要做出取舍。不要盲目从在线程序库或讨论论坛拷贝正则表达式。务必在您自己的数据和应用程序上测试它们。

如何尋找或驗證電子郵件地址
  • 简
  • 繁
  • En
關於正規表示式 » 正規表示式範例 » 如何尋找或驗證電子郵件地址

範例
正規表示式範例
數字範圍
浮點數
電子郵件地址
IP 位址
有效日期
數字日期轉文字
信用卡號碼
比對完整行
刪除重複行
程式設計
兩個相近的字詞
陷阱
災難性的回溯
過多重複
拒絕服務
讓所有內容都可選
重複擷取群組
Unicode 和 8 位元混合
本網站的更多資訊
簡介
正規表示式快速入門
正規表示式教學
替換字串教學
應用程式和語言
正規表示式範例
正規表示式參考
替換字串參考

如何尋找或驗證電子郵件地址

我收到最多回饋的正規表示式,更別提「錯誤」回報,就是您會在本網站的首頁上找到的這個:\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b。我聲稱這個正規表示式可以比對任何電子郵件地址。我收到的回饋大多反駁這個說法,並指出這個正規表示式無法比對的電子郵件地址。通常,「錯誤」回報也會包含建議,讓這個正規表示式變得「完美」。

正如我在下方說明的,我的說法只有在我接受對有效電子郵件地址的定義,以及什麼不是有效電子郵件地址的定義時,才成立。如果您想使用不同的定義,您必須調整正規表示式。比對有效的電子郵件地址是一個完美的範例,說明 (1) 在撰寫正規表示式之前,您必須確切知道您要比對什麼,以及什麼不要比對;(2) 精確性和實用性之間通常會有取捨。

上述正規表示式的優點在於它可以比對當今使用中的 99% 電子郵件地址。它比對的所有電子郵件地址都可以由 99% 的所有電子郵件軟體處理。如果您正在尋找快速解決方案,您只需要閱讀下一段落。如果您想了解所有取捨,並從中選擇大量替代方案,請繼續閱讀。

如果您想使用上述正規表示式,您需要了解兩件事。首先,長正規表示式會讓段落難以正確格式化。因此,我在三個字元類別中都沒有包含 a-z。此正規表示式旨在與您的正規表示式引擎的「不分大小寫」選項一起使用。(您會訝異我收到多少關於此的「錯誤」報告。)其次,上述正規表示式以 字元邊界 為界定,這使其適用於從檔案或較大區塊文字中萃取電子郵件地址。如果您想檢查使用者是否輸入有效的電子郵件地址,請將字元邊界替換為 字串開頭和字串結尾錨點,如下所示:^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$。

前一段落也適用於以下所有範例。您可能需要將字元邊界變更為字串開頭/結尾錨點,或反之亦然。而且您必須開啟不分大小寫比對選項。

驗證電子郵件地址的取捨

在 ICANN 讓任何資金雄厚的公司都能建立自己的頂層網域之前,最長的頂層網域是鮮少使用的 .museum 和 .travel,長度為 6 個字母。最常見的頂層網域長度為 2 個字母,用於特定國家的網域,以及 3 或 4 個字母,用於一般用途的網域,例如 .com 和 .info。您會在各種正規表示式教學課程和參考中找到許多用於驗證電子郵件地址的正規表示式,這些正規表示式仍然假設頂層網域相當短。較舊版本的此正規表示式教學課程在引言中提到 \b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b 作為電子郵件地址的正規表示式。這個正規表示式和本頁頂端的正規表示式只有一個小差異。正規表示式結尾的 4 將頂層網域限制為 4 個字元。如果您使用這個正規表示式和錨點來驗證訂單表單中輸入的電子郵件地址,fabio@domain.solutions 就必須到其他地方購物。是的,.solutions TLD 存在,而我在撰寫本文時,domain.solutions 每年售價 16.88 美元。

如果您想對頂層網域比 [A-Z]{2,} 更加嚴格,^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,63}$ 是您實際上能達到的極限。網域名稱的每個部分長度不得超過 63 個字元。沒有單一數字的頂層網域,也沒有任何頂層網域包含數字。看起來 ICANN 也不會核准此類網域。

電子郵件地址可以位於子網域的伺服器上,如 john@server.company.domain.com。由於我在 @ 符號後的字元類別中包含一個點,因此所有上述正規表示式都符合此電子郵件地址。但上述正規表示式也符合 john@domain...com,由於連續的點,因此無效。你可以透過在上述任何正規表示式中將 [A-Z0-9.-]+\. 替換為 (?:[A-Z0-9-]+\.)+ 來排除此類符合項。我從字元類別中移除點,而重複使用字元類別和後面的文字點。例如,^[A-Z0-9._%+-]+@(?:[A-Z0-9-]+\.)+[A-Z]{2,}$ 符合 john@server.company.domain.com,但不符合 john@domain...com。

如果你想避免系統因任意大的輸入而中斷,你可以將無限 量詞 替換為有限量詞。^[A-Z0-9._%+-]{1,64}@(?:[A-Z0-9-]{1,63}\.){1,125}[A-Z]{2,63}$ 考量到本地端 (在 @ 之前) 限制為 64 個字元,而網域名稱的每個部分限制為 63 個字元。子網域的數量沒有直接限制。但 SMTP 可以處理的電子郵件地址最大長度為 254 個字元。因此,如果本地端為單一字元、頂層網域為兩個字元,且子網域為單一字元,則子網域的最大數量為 125。

以前的正則表達式實際上並沒有將電子郵件位址限制為 254 個字元。如果每個部分都達到其最大長度,則正則表示式可以匹配長度不超過8129個字元的字串。您可以通過將允許的子域數量從125個減少到更現實的值(如8個)來減少這種情況。我從未見過包含超過 4 個子域的電子郵件位址。如果要強制執行 254 個字元的限制,最好的解決方案是在使用正則表示式之前檢查輸入字串的長度。雖然這需要幾行過程代碼,但檢查字串的長度幾乎是瞬間的。如果只能使用正則表達式,則可以使用 ^[A-Z0-9@._%+-]{6,254}$ 作為第一次傳遞,以確保字符串不包含無效字元,並且不會太短或太長。如果你需要用一個正則表達式做所有事情,你就需要一個支援前瞻的正則表達式風格。正則表達式 ^(?=[A-Z0-9@._%+-]{6,254}$)[A-Z0-9._%+-]{1,64}@(?:[A-Z0-9-]{1,63}\.){1,8}[A-Z]{2,63}$ 使用 lookahead 首先檢查字串是否不包含無效字元,以及是否太短或太長。當前瞻成功時,正則表達式的其餘部分將第二次遍曆字串,以檢查 @ 符號和點的位置是否正確。

這些正則表示式都允許在本地端部分的任何地方使用字元 ._%+-。你可以使用 ^[A-Z0-9][A-Z0-9._%+-]{0,63} 來強制本地端部分以字母開頭,而不是使用 ^[A-Z0-9._%+-]{1,64} 作為本地端部分:^[A-Z0-9][A-Z0-9._%+-]{0,63}@(?:[A-Z0-9-]{1,63}\.){1,125}[A-Z]{2,63}$。在使用向前預查來檢查地址的整體長度時,可以在向前預查中檢查第一個字元。在檢查本地端部分的長度時,我們不需要重複初始字元檢查。這個正則表示式太長了,不適合頁面的寬度,所以讓我們開啟自由間距模式

^(?=[A-Z0-9][A-Z0-9@._%+-]{5,253}$)
[A-Z0-9._%+-]{1,64}@(?:[A-Z0-9-]{1,63}\.){1,8}[A-Z]{2,63}$

網域名稱可以包含連字號。但它們不能以連字號開頭或結尾。[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])? 匹配長度在 1 到 63 個字元之間且以字母或數字開頭和結尾的網域名稱。非擷取群組使網域名稱中間和最後一個字母或數字作為一個整體是可選的,以確保我們允許單字元網域名稱,同時確保包含兩個或多個字元的網域名稱不會以連字號結尾。整體正則表示式開始變得相當複雜

^[A-Z0-9][A-Z0-9._%+-]{0,63}@
(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.){1,8}[A-Z]{2,63}$

功能變數名稱不能包含連續的連字元。[A-Z0-9]+(?:-[A-Z0-9]+)* 匹配以字母或數字開頭和結尾的域名,並且包含任意數量的非連續連字元。這是最有效的方法。此正則表達式不會執行任何回溯以匹配有效的功能變數名稱。它匹配功能變數名稱開頭的所有字母和數位。如果沒有連字元,則後面的可選組將立即失敗。如果有連字元,則該組將匹配每個連字元,後跟所有字母和數位,直到下一個連字元或功能變數名稱的末尾。當連字元必須與字母或數位配對時,我們無法強制執行最大長度,但字母和數位可以獨立存在。但是,我們可以使用用於強制執行電子郵件地址總長度的前瞻技術來強制執行域名的長度,同時不允許連續的連字元:(?=[A-Z0-9-]{1,63}\。)[A-Z0-9]+(?:-[A-Z0-9]+)*.請注意,lookahead 還會檢查當功能變數名稱在電子郵件位址中完全限定時必須出現在域名之後的點。這很重要。如果不檢查點,lookahead 將接受更長的功能變數名稱。由於 lookahead 不使用它匹配的文字,因此點不包含在此正則表達式的整體匹配中。當我們將此正規表示式放入電子郵件地址的整體正則表示式中時,點將像在前面的正規表示式中一樣匹配:

^[A-Z0-9][A-Z0-9._%+-]{0,63}@
(?:(?=[A-Z0-9-]{1,63}\.)[A-Z0-9]+(?:-[A-Z0-9]+)*\.){1,8}[A-Z]{2,63}$

如果我們包含前瞻來檢查整體長度,我們的正規表示式會在本地端部分執行兩次,在網域名稱執行三次,以驗證所有內容

^(?=[A-Z0-9][A-Z0-9@._%+-]{5,253}$)[A-Z0-9._%+-]{1,64}@
(?:(?=[A-Z0-9-]{1,63}\.)[A-Z0-9]+(?:-[A-Z0-9]+)*\.){1,8}[A-Z]{2,63}$

在現代的個人電腦或伺服器上,當驗證單一 254 字元的電子郵件地址時,這個正規表示式將執行得很好。拒絕較長的輸入會更快,因為當先行斷言在第一次通過時失敗時,正規表示式就會失敗。但我不會建議使用像這樣複雜的正規表示式來透過大量文件或信件存檔來搜尋電子郵件地址。你最好使用此頁面頂端的簡單正規表示式來快速收集看起來像電子郵件地址的所有內容。對結果進行重複資料刪除,然後如果你想進一步篩選出無效的地址,請使用更嚴格的正規表示式。

說到回溯,此頁面上的正規表示式都沒有進行任何回溯來比對有效的電子郵件地址。但特別是後者,可能會對不是有效電子郵件地址的東西進行相當多的回溯。如果你的正規表示式風格支援佔有量詞,你可以透過讓所有量詞都變成佔有量詞來消除所有回溯。因為不需要回溯來尋找比對,所以這樣做不會改變這些正規表示式比對的內容。它只允許它們在輸入不是有效的電子郵件地址時更快地失敗。正確處理次網域的最簡單正規表示式隨後變成 ^[A-Z0-9._%+-]++@(?:[A-Z0-9-]++\.)++[A-Z]{2,}+$,在每個量詞後加上一個額外的 +。我們可以對我們最複雜的正規表示式執行相同的操作

^(?=[A-Z0-9][A-Z0-9@._%+-]{5,253}+$)[A-Z0-9._%+-]{1,64}+@
(?:(?=[A-Z0-9-]{1,63}+\.)[A-Z0-9]++(?:-[A-Z0-9]++)*+\.){1,8}+[A-Z]{2,63}+$

所有這些正規表示式中的一個重要取捨是,它們只允許英文字母、數字和最常用的特殊符號。主要原因是我不相信我所有的電子郵件軟體都能處理其他內容。即使 John.O'Hara@domain.com 是語法上有效的電子郵件地址,但有些軟體可能會誤將撇號解釋為分隔符號。例如,將此電子郵件地址盲目插入 SQL 查詢中,最好的情況是當字串以單引號分隔時會導致失敗,最壞的情況是會讓你的網站面臨 SQL 注入攻擊。

當然,網域名稱已經可以包含非英文字元很多年了。但大多數軟體仍然堅持西方程式設計師習慣使用的 37 個字元。支援國際化網域會開啟一個問題,也就是非 ASCII 字元應該如何編碼。因此,如果你使用此頁面上的任何正規表示式,任何具有 @โดเมน.ไทย 地址的人都會很倒楣。

結論是,要決定使用哪個正規表示式,無論你是要比對電子郵件地址或其他定義模糊的東西,你都需要先考慮所有取捨。比對到無效的東西有多糟?不比對到有效的東西有多糟?你的正規表示式可以有多複雜?如果你必須在稍後變更正規表示式,因為它太廣泛或太狹窄,那會有多昂貴?這些問題的不同答案將需要不同的正規表示式作為解決方案。我的電子郵件正規表示式可以滿足我的需求,但它可能無法滿足你的需求。

正規表示式不會傳送電子郵件

不要過度嘗試使用正規表示法來消除無效的電子郵件地址。原因在於,在嘗試寄送電子郵件之前,你並不知道一個地址是否有效。而且,即使如此,這可能還不夠。即使電子郵件已送達某個信箱,這並不表示有人會讀取該信箱。如果你真的需要確定某個電子郵件地址是否有效,你必須寄送一封電子郵件給該地址,其中包含一個代碼或連結,讓收件者執行第二個驗證步驟。如果你這麼做,那麼使用可能會拒絕有效電子郵件地址的正規表示法就沒有什麼意義了。

相同的原則適用於許多情況。在嘗試比對有效日期時,通常使用一點算術來檢查閏年會比較容易,而不是嘗試在正規表示法中執行此操作。使用正規表示法來尋找潛在的比對或檢查輸入是否使用正確的語法,並對正規表示法傳回的潛在比對執行實際驗證。正規表示法是一個強大的工具,但它們遠非萬靈丹。

官方標準:RFC 5322

你可能想知道為什麼沒有「官方」的萬無一失正規表示法來比對電子郵件地址。嗯,有一個官方定義,但它並非萬無一失。

官方標準稱為RFC 5322。它描述了有效電子郵件地址必須遵守的語法。你可以(但你不應該這麼做,請繼續閱讀)使用以下正規表示法來實作它。RFC 5322 讓網域名稱部分開放給實作特定的選擇,這些選擇在今日的網際網路上無法運作。正規表示法實作了RFC 1035中的「首選」語法,這是 RFC 5322 中的建議之一。

\A(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*
 
|  "(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]
      
|  \\[\x01-\x09\x0b\x0c\x0e-\x7f])*")
@ (?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?
  
|  \[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}
       
(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:
          
(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]
          
|  \\[\x01-\x09\x0b\x0c\x0e-\x7f])+)
     
\])\z

此正規表示法分為兩部分:@ 符號之前的部分和 @ 符號之後的部分。@ 符號之前的部分有兩個選項。第一個選項允許它包含一系列字母、數字和某些符號,包括一個或多個點。但是,點不能連續出現,也不能出現在電子郵件地址的開頭或結尾。另一個選項要求 @ 符號之前的部分用雙引號括起來,允許在引號之間使用任何 ASCII 字元串。空白字元、雙引號和反斜線必須用反斜線進行跳脫。

@ 符號之後的部分也有兩個選項。它可以是完全限定的網域名稱,或者可以是方括號中的文字網際網路地址。文字網際網路地址可以是 IP 地址,也可以是特定網域的路由地址。

不應使用此正規表示法的原因是它過於廣泛。您的應用程式可能無法處理此正規表示法允許的所有電子郵件地址。特定網域的路由地址可能包含不可列印的 ASCII 控制字元,如果您的應用程式需要顯示地址,這可能會造成問題。並非所有應用程式都支援使用雙引號或方括號的本地部分語法。事實上,RFC 5322 本身將使用方括號的表示法標記為過時。

如果我們省略 IP 地址、特定網域地址、使用雙引號和方括號的語法,我們將獲得更實用的 RFC 5322 實作。它仍然會符合當今實際使用中 99.99% 的所有電子郵件地址。

\A[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@
(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\z

這兩個正規表示法都不對整體電子郵件地址、本地部分或網域名稱強制長度限制。RFC 5322 沒有指定任何長度限制。這些限制源於其他協定的限制,例如實際傳送電子郵件的 SMTP 協定。RFC 1035 確實指出網域必須為 63 個字元或更少,但沒有將其包含在其語法規範中。原因是真正的正規語言無法同時強制長度限制和禁止連續連字號。但現代正規表示法並非真正的正規表示法,因此我們可以使用前瞻檢查來新增長度限制檢查,就像我們之前所做的那樣

\A(?=[a-z0-9@.!#$%&'*+/=?^_`{|}~-]{6,254}\z)
 
(?=[a-z0-9.!#$%&'*+/=?^_`{|}~-]{1,64}@)
 
[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*
@ (?:(?=[a-z0-9-]{1,63}\.)[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+
  
(?=[a-z0-9-]{1,63}\z)[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\z

因此,即使遵循官方標準,仍然需要做出取捨。不要盲目從線上程式庫或討論論壇複製正規表示法。務必在您自己的資料和應用程式上測試它們。

How to Find or Validate an Email Address
  • 简
  • 繁
  • En
About Regular Expressions » Sample Regular Expressions » How to Find or Validate an Email Address

Examples
Regular Expressions Examples
Numeric Ranges
Floating Point Numbers
Email Addresses
IP Addresses
Valid Dates
Numeric Dates to Text
Credit Card Numbers
Matching Complete Lines
Deleting Duplicate Lines
Programming
Two Near Words
Pitfalls
Catastrophic Backtracking
Too Many Repetitions
Denial of Service
Making Everything Optional
Repeated Capturing Group
Mixing Unicode & 8-bit
More on This Site
Introduction
Regular Expressions Quick Start
Regular Expressions Tutorial
Replacement Strings Tutorial
Applications and Languages
Regular Expressions Examples
Regular Expressions Reference
Replacement Strings Reference

How to Find or Validate an Email Address

The regular expression I receive the most feedback, not to mention “bug” reports on, is the one you’ll find right on this site’s home page: \b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b. This regular expression, I claim, matches any email address. Most of the feedback I get refutes that claim by showing one email address that this regex doesn’t match. Usually, the “bug” report also includes a suggestion to make the regex “perfect”.

As I explain below, my claim only holds true when one accepts my definition of what a valid email address really is, and what it’s not. If you want to use a different definition, you’ll have to adapt the regex. Matching a valid email address is a perfect example showing that (1) before writing a regex, you have to know exactly what you’re trying to match, and what not; and (2) there’s often a trade-off between what’s exact, and what’s practical.

The virtue of my regular expression above is that it matches 99% of the email addresses in use today. All the email addresses it matches can be handled by 99% of all email software out there. If you’re looking for a quick solution, you only need to read the next paragraph. If you want to know all the trade-offs and get plenty of alternatives to choose from, read on.

If you want to use the regular expression above, there are two things you need to understand. First, long regexes make it difficult to nicely format paragraphs. So I didn’t include a-z in any of the three character classes. This regex is intended to be used with your regex engine’s “case insensitive” option turned on. (You’d be surprised how many “bug” reports I get about that.) Second, the above regex is delimited with word boundaries, which makes it suitable for extracting email addresses from files or larger blocks of text. If you want to check whether the user typed in a valid email address, replace the word boundaries with start-of-string and end-of-string anchors, like this: ^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$.

The previous paragraph also applies to all of the following examples. You may need to change word boundaries into start/end-of-string anchors, or vice versa. And you have to turn on the case insensitive matching option.

Trade-Offs in Validating Email Addresses

Before ICANN made it possible for any well-funded company to create their own top-level domains, the longest top-level domains were the rarely used .museum and .travel which are 6 letters long. The most common top-level domains were 2 letters long for country-specific domains, and 3 or 4 letters long for general-purpose domains like .com and .info. A lot of regexes for validating email addresses you’ll find in various regex tutorials and references still assume the top-level domain to be fairly short. Older editions of this regex tutorial mentioned \b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b as the regex for email addresses in its introduction. There’s only one little difference between this regex and the one at the top of this page. The 4 at the end of the regex restricts the top-level domain to 4 characters. If you use this regex with anchors to validate the email address entered on your order form, fabio@domain.solutions has to do his shopping elsewhere. Yes, the .solutions TLD exists and when I write this, domain.solutions can be yours for $16.88 per year.

If you want to be more strict than [A-Z]{2,} for the top-level domain, ^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,63}$ is as far as you can practically go. Each part of a domain name can be no longer than 63 characters. There are no single-digit top-level domains and none contain digits. It doesn’t look like ICANN will approve such domains either.

Email addresses can be on servers on a subdomain as in john@server.company.domain.com. All of the above regexes match this email address, because I included a dot in the character class after the @ symbol. But the above regexes also match john@domain...com which is not valid due to the consecutive dots. You can exclude such matches by replacing [A-Z0-9.-]+\. with (?:[A-Z0-9-]+\.)+ in any of the above regexes. I removed the dot from the character class and instead repeated the character class and the following literal dot. E.g. ^[A-Z0-9._%+-]+@(?:[A-Z0-9-]+\.)+[A-Z]{2,}$ matches john@server.company.domain.com but not john@domain...com.

If you want to avoid your system choking on arbitrarily large input, you can replace the infinite quantifiers with finite ones. ^[A-Z0-9._%+-]{1,64}@(?:[A-Z0-9-]{1,63}\.){1,125}[A-Z]{2,63}$ takes into account that the local part (before the @) is limited to 64 characters and that each part of the domain name is limited to 63 characters. There’s no direct limit on the number of subdomains. But the maximum length of an email address that can be handled by SMTP is 254 characters. So with a single-character local part, a two-letter top-level domain and single-character sub-domains, 125 is the maximum number of sub-domains.

The previous regex does not actually limit email addresses to 254 characters. If each part is at its maximum length, the regex can match strings up to 8129 characters in length. You can reduce that by lowering the number of allowed sub-domains from 125 to something more realistic like 8. I’ve never seen an email address with more than 4 subdomains. If you want to enforce the 254 character limit, the best solution is to check the length of the input string before you even use a regex. Though this requires a few lines of procedural code, checking the length of a string is near-instantaneous. If you can only use regexes, ^[A-Z0-9@._%+-]{6,254}$ can be used as a first pass to make sure the string doesn’t contain invalid characters and isn’t too short or too long. If you need to do everything with one regex, you’ll need a regex flavor that supports lookahead. The regular expression ^(?=[A-Z0-9@._%+-]{6,254}$)[A-Z0-9._%+-]{1,64}@(?:[A-Z0-9-]{1,63}\.){1,8}[A-Z]{2,63}$ uses a lookahead to first check that the string doesn’t contain invalid characters and isn’t too short or too long. When the lookahead succeeds, the remainder of the regex makes a second pass over the string to check for proper placement of the @ sign and the dots.

All of these regexes allow the characters ._%+- anywhere in the local part. You can force the local part to begin with a letter by using ^[A-Z0-9][A-Z0-9._%+-]{0,63} instead of ^[A-Z0-9._%+-]{1,64} for the local part: ^[A-Z0-9][A-Z0-9._%+-]{0,63}@(?:[A-Z0-9-]{1,63}\.){1,125}[A-Z]{2,63}$. When using lookahead to check the overall length of the address, the first character can be checked in the lookahead. We don’t need to repeat the initial character check when checking the length of the local part. This regex is too long to fit the width of the page, so let’s turn on free-spacing mode:

^(?=[A-Z0-9][A-Z0-9@._%+-]{5,253}$)
[A-Z0-9._%+-]{1,64}@(?:[A-Z0-9-]{1,63}\.){1,8}[A-Z]{2,63}$

Domain names can contain hyphens. But they cannot begin or end with a hyphen. [A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])? matches a domain name between 1 and 63 characters long that starts and ends with a letter or digit. The non-capturing group makes the middle of the domain and the final letter or digit optional as a whole to ensure that we allow single-character domains while at the same time ensuring that domains with two or more characters do not end with a hyphen. The overall regex starts to get quite complicated:

^[A-Z0-9][A-Z0-9._%+-]{0,63}@
(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.){1,8}[A-Z]{2,63}$

Domain names cannot contain consecutive hyphens. [A-Z0-9]+(?:-[A-Z0-9]+)* matches a domain name that starts and ends with a letter or digit and that contains any number of non-consecutive hyphens. This is the most efficient way. This regex does not do any backtracking to match a valid domain name. It matches all letters and digits at the start of the domain name. If there are no hyphens, the optional group that follows fails immediately. If there are hyphens, the group matches each hyphen followed by all letters and digits up to the next hyphen or the end of the domain name. We can’t enforce the maximum length when hyphens must be paired with a letter or digit, but letters and digits can stand on their own. But we can use the lookahead technique that we used to enforce the overall length of the email address to enforce the length of the domain name while disallowing consecutive hyphens: (?=[A-Z0-9-]{1,63}\.)[A-Z0-9]+(?:-[A-Z0-9]+)*. Notice that the lookahead also checks for the dot that must appear after the domain name when it is fully qualified in an email address. This is important. Without checking for the dot, the lookahead would accept longer domain names. Since the lookahead does not consume the text it matches, the dot is not included in the overall match of this regex. When we put this regex into the overall regex for email addresses, the dot will be matched as it was in the previous regexes:

^[A-Z0-9][A-Z0-9._%+-]{0,63}@
(?:(?=[A-Z0-9-]{1,63}\.)[A-Z0-9]+(?:-[A-Z0-9]+)*\.){1,8}[A-Z]{2,63}$

If we include the lookahead to check the overall length, our regex makes two passes over the local part, and three passes over the domain names to validate everything:

^(?=[A-Z0-9][A-Z0-9@._%+-]{5,253}$)[A-Z0-9._%+-]{1,64}@
(?:(?=[A-Z0-9-]{1,63}\.)[A-Z0-9]+(?:-[A-Z0-9]+)*\.){1,8}[A-Z]{2,63}$

On a modern PC or server this regex will perform just fine when validating a single 254-character email address. Rejecting longer input would even be faster because the regex will fail when the lookahead fails during first pass. But I wouldn’t recommend using a regex as complex as this to search for email addresses through a large archive of documents or correspondence. You’re better off using the simple regex at the top of this page to quickly gather everything that looks like an email address. Deduplicate the results and then use a stricter regex if you want to further filter out invalid addresses.

And speaking of backtracking, none of the regexes on this page do any backtracking to match valid email addresses. But particularly the latter ones may do a fair bit of backtracking on something that’s not quite a valid email address. If your regex flavor supports possessive quantifiers, you can eliminate all backtracking by making all quantifiers possessive. Because no backtracking is needed to find matches, doing this does not change what is matched by these regexes. It only allows them to fail faster when the input is not a valid email address. The simplest regex that correctly handles subdomains then becomes ^[A-Z0-9._%+-]++@(?:[A-Z0-9-]++\.)++[A-Z]{2,}+$ with an extra + after each quantifier. We can do the same with our most complex regex:

^(?=[A-Z0-9][A-Z0-9@._%+-]{5,253}+$)[A-Z0-9._%+-]{1,64}+@
(?:(?=[A-Z0-9-]{1,63}+\.)[A-Z0-9]++(?:-[A-Z0-9]++)*+\.){1,8}+[A-Z]{2,63}+$

An important trade-off in all these regexes is that they only allow English letters, digits, and the most commonly used special symbols. The main reason is that I don’t trust all my email software to be able to handle much else. Even though John.O'Hara@domain.com is a syntactically valid email address, there’s a risk that some software will misinterpret the apostrophe as a delimiting quote. Blindly inserting this email address into an SQL query, for example, will at best cause it to fail when strings are delimited with single quotes and at worst open your site up to SQL injection attacks.

And of course, it’s been many years already that domain names can include non-English characters. But most software still sticks to the 37 characters Western programmers are used to. Supporting internationalized domains opens up a whole can of worms of how the non-ASCII characters should be encoded. So if you use any of the regexes on this page, anyone with an @โดเมน.ไทย address will be out of luck.

The conclusion is that to decide which regular expression to use, whether you’re trying to match an email address or something else that’s vaguely defined, you need to start with considering all the trade-offs. How bad is it to match something that’s not valid? How bad is it not to match something that is valid? How complex can your regular expression be? How expensive would it be if you had to change the regular expression later because it turned out to be too broad or too narrow? Different answers to these questions will require a different regular expression as the solution. My email regex does what I want, but it may not do what you want.

Regexes Don’t Send Email

Don’t go overboard in trying to eliminate invalid email addresses with your regular expression. The reason is that you don’t really know whether an address is valid until you try to send an email to it. And even that might not be enough. Even if the email arrives in a mailbox, that doesn’t mean somebody still reads that mailbox. If you really need to be sure an email address is valid, you’ll need to send an email to it that contains a code or link for the recipient to perform a second authentication step. And if you’re doing that, then there is little point in using a regex that may reject valid email addresses.

The same principle applies in many situations. When trying to match a valid date, it’s often easier to use a bit of arithmetic to check for leap years, rather than trying to do it in a regex. Use a regular expression to find potential matches or check if the input uses the proper syntax, and do the actual validation on the potential matches returned by the regular expression. Regular expressions are a powerful tool, but they’re far from a panacea.

The Official Standard: RFC 5322

Maybe you’re wondering why there’s no “official” fool-proof regex to match email addresses. Well, there is an official definition, but it’s hardly fool-proof.

The official standard is known as RFC 5322. It describes the syntax that valid email addresses must adhere to. You can (but you shouldn’t—read on) implement it with the following regular expression. RFC 5322 leaves the domain name part open to implementation-specific choices that won’t work on the Internet today. The regex implements the “preferred” syntax from RFC 1035 which is one of the recommendations in RFC 5322:

\A(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*
 
|  "(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]
      
|  \\[\x01-\x09\x0b\x0c\x0e-\x7f])*")
@ (?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?
  
|  \[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}
       
(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:
          
(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]
          
|  \\[\x01-\x09\x0b\x0c\x0e-\x7f])+)
     
\])\z

This regex has two parts: the part before the @, and the part after the @. There are two alternatives for the part before the @. The first alternative allows it to consist of a series of letters, digits and certain symbols, including one or more dots. However, dots may not appear consecutively or at the start or end of the email address. The other alternative requires the part before the @ to be enclosed in double quotes, allowing any string of ASCII characters between the quotes. Whitespace characters, double quotes and backslashes must be escaped with backslashes.

The part after the @ also has two alternatives. It can either be a fully qualified domain name, or it can be a literal Internet address between square brackets. The literal Internet address can either be an IP address, or a domain-specific routing address.

The reason you shouldn’t use this regex is that it is overly broad. Your application may not be able to handle all email addresses this regex allows. Domain-specific routing addresses can contain non-printable ASCII control characters, which can cause trouble if your application needs to display addresses. Not all applications support the syntax for the local part using double quotes or square brackets. In fact, RFC 5322 itself marks the notation using square brackets as obsolete.

We get a more practical implementation of RFC 5322 if we omit IP addresses, domain-specific addresses, the syntax using double quotes and square brackets. It will still match 99.99% of all email addresses in actual use today.

\A[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@
(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\z

Neither of these regexes enforce length limits on the overall email address or the local part or the domain names. RFC 5322 does not specify any length limitations. Those stem from limitations in other protocols like the SMTP protocol for actually sending email. RFC 1035 does state that domains must be 63 characters or less, but does not include that in its syntax specification. The reason is that a true regular language cannot enforce a length limit and disallow consecutive hyphens at the same time. But modern regex flavors aren’t truly regular, so we can add length limit checks using lookahead like we did before:

\A(?=[a-z0-9@.!#$%&'*+/=?^_`{|}~-]{6,254}\z)
 
(?=[a-z0-9.!#$%&'*+/=?^_`{|}~-]{1,64}@)
 
[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*
@ (?:(?=[a-z0-9-]{1,63}\.)[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+
  
(?=[a-z0-9-]{1,63}\z)[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\z

So even when following official standards, there are still trade-offs to be made. Don’t blindly copy regular expressions from online libraries or discussion forums. Always test them on your own data and with your own applications.

©2015-2025 艾丽卡 support@alaica.com