示例 |
正则表达式示例 |
数字范围 |
浮点数 |
电子邮件地址 |
IP 地址 |
有效日期 |
数字日期转文本 |
信用卡号 |
匹配完整行 |
删除重复行 |
编程 |
两个相邻单词 |
陷阱 |
灾难性回溯 |
重复过多 |
拒绝服务 |
使所有内容可选 |
重复捕获组 |
混合 Unicode 和 8 位 |
更多内容 |
简介 |
正则表达式快速入门 |
正则表达式教程 |
替换字符串教程 |
应用程序和语言 |
正则表达式示例 |
正则表达式参考 |
替换字符串参考 |
书籍评论 |
可打印 PDF |
关于本网站 |
RSS 提要和博客 |
我收到最多反馈(更不用说“错误”报告)的正则表达式是您可以在本网站的主页上找到的表达式:\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 个字母长,针对 .com 和 .info 等通用域名为 3 或 4 个字母长。您在各种正则表达式教程和参考中找到的用于验证电子邮件地址的许多正则表达式仍然假设顶级域相当短。此正则表达式教程 的较早版本在其引言中将 \b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b 作为电子邮件地址的正则表达式。此正则表达式与本页顶部的正则表达式之间只有一个很小的区别。正则表达式末尾的 4 将顶级域限制为 4 个字符。如果您将此正则表达式与锚点一起用于验证在您的订单表单中输入的电子邮件地址,则 [email protected] 必须到其他地方购物。是的,.solutions TLD 存在,当我写这篇文章时,disaproved.solutions 每年只需 16.88 美元即可成为您的。
如果你想对顶级域的限制比 [A-Z]{2,} 更严格,^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,63}$ 就是你实际能达到的最严格程度了。域名中的每个部分都不能超过 63 个字符。没有单数字符的顶级域,也没有包含数字的顶级域。看起来 ICANN 也不会批准此类域名。
电子邮件地址可以位于子域中的服务器上,如 [email protected]。由于我在 @ 符号后的字符类中包含了一个点,因此上述所有正则表达式都匹配此电子邮件地址。但上述正则表达式也匹配 [email protected],由于连续的点,因此该地址无效。你可以将 [A-Z0-9.-]+\. 替换为 (?:[A-Z0-9-]+\.)+,以在上述任何正则表达式中排除此类匹配。我从字符类中删除了点,而是重复了字符类和随后的点字面值。例如,^[A-Z0-9._%+-]+@(?:[A-Z0-9-]+\.)+[A-Z]{2,}$ 匹配 [email protected] 但不匹配 [email protected]。
如果您想避免系统因任意大的输入而崩溃,则可以用有限量词替换无限量词。^[A-Z0-9._%+-]{1,64}@(?:[A-Z0-9-]{1,63}\.){1,125}[A-Z]{2,63}$考虑到本地部分(在 @ 之前)限制为 64 个字符,且域名中的每个部分限制为 63 个字符。子域名的数量没有直接限制。但 SMTP 可处理的电子邮件地址的最大长度为 254 个字符。因此,如果本地部分为单个字符,顶级域为两个字母,子域名也为单个字符,则子域名的最大数量为 125。
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.
所有这些正则表达式都允许在本地部分的任何位置使用字符 ._%+-。您可以通过使用 ^[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}$
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}$
如果我们包括预查来检查整体长度,我们的正则表达式会对本地部分进行两次扫描,对域名进行三次扫描以验证所有内容
^(?=[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}$
在现代 PC 或服务器上,在验证单个 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'[email protected] 是一个语法上有效的电子邮件地址,但仍有可能一些软件会将撇号误认为分隔符。例如,将此电子邮件地址盲目插入 SQL 查询中,最好的情况是当字符串用单引号分隔时导致查询失败,最坏的情况是让你的网站面临 SQL 注入攻击。
当然,很多年来,域名都可以包含非英语字符。但大多数软件仍然坚持 37 个西方程序员习惯使用的字符。支持国际化域名会打开一个难题,即如何对非 ASCII 字符进行编码。因此,如果你使用此页面上的任何正则表达式,任何具有
结论是,要决定使用哪个正则表达式(无论你是想匹配电子邮件地址还是其他一些定义模糊的内容),你需要首先考虑所有权衡。匹配无效内容有多糟糕?不匹配有效内容有多糟糕?你的正则表达式可以有多复杂?如果你不得不在以后更改正则表达式,因为它太宽泛或太狭窄,那将有多昂贵?对这些问题的不同回答将需要不同的正则表达式作为解决方案。我的电子邮件正则表达式可以满足我的需求,但它可能无法满足你的需求。
不要过度尝试使用正则表达式来消除无效的电子邮件地址。原因在于,在你尝试向某个地址发送电子邮件之前,你并不知道该地址是否有效。即使这样也可能不够。即使电子邮件到达了某个邮箱,这并不意味着有人仍在阅读该邮箱。如果你真的需要确保某个电子邮件地址有效,你需要向该地址发送一封电子邮件,其中包含一个代码或链接,以便收件人执行第二次身份验证步骤。如果你这样做,那么使用可能会拒绝有效电子邮件地址的正则表达式就没有什么意义了。
同样的原则适用于许多情况。在尝试匹配有效日期时,通常更容易使用一些算法来检查闰年,而不是尝试在正则表达式中执行此操作。使用正则表达式查找潜在匹配项或检查输入是否使用正确的语法,并对正则表达式返回的潜在匹配项执行实际验证。正则表达式是一个强大的工具,但它们远非万能药。
也许你很好奇为什么没有“官方”万无一失的正则表达式来匹配电子邮件地址。嗯,有一个官方定义,但它绝非万无一失。
官方标准称为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 字符串。空格字符、双引号和反斜杠必须用反斜杠转义。
@ 后面的部分也有两种选择。它既可以是完全限定的域名(例如 regular-expressions.info),也可以是方括号中的文字互联网地址。文字互联网地址既可以是 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
因此,即使遵循官方标准,仍然需要做出权衡。不要盲目从在线库或讨论论坛中复制正则表达式。始终在您自己的数据和应用程序中对其进行测试。
| 快速入门 | 教程 | 工具和语言 | 示例 | 参考 | 书籍评论 |
| 正则表达式示例 | 数字范围 | 浮点数 | 电子邮件地址 | IP 地址 | 有效日期 | 数字日期转文本 | 信用卡号 | 匹配完整行 | 删除重复行 | 编程 | 两个相近的单词 |
| 灾难性回溯 | 重复次数过多 | 拒绝服务 | 使所有内容变为可选 | 重复捕获组 | 混合 Unicode 和 8 位 |
页面 URL:https://regexper.cn/email.html
页面最后更新时间:2023 年 8 月 29 日
网站最后更新时间:2024 年 3 月 15 日
版权所有 © 2003-2024 Jan Goyvaerts。保留所有权利。