多年来,我慢慢地开发了一个正则表达式,它可以正确验证大多数电子邮件地址,假设它们不使用IP地址作为服务器部分。

我在几个PHP程序中使用它,而且大多数时候都可以使用。然而,不时有人与我联系,他们对使用它的网站有问题,我最终不得不做出一些调整(最近我意识到我不允许四个字符的TLD)。

你有或见过验证电子邮件的最佳正则表达式是什么?

我见过几种使用函数的解决方案,这些函数使用了几个较短的表达式,但我宁愿在简单函数中使用一个长的复杂表达式,而不是在更复杂的函数中使用几个短表达式。


当前回答

完全符合RFC 822的正则表达式由于其长度而效率低下且晦涩难懂。幸运的是,RFC 822被两次取代,当前的电子邮件地址规范是RFC 5322。RFC 5322产生了一个正则表达式,如果研究几分钟就可以理解,并且对于实际使用来说足够有效。

一个符合RFC 5322的正则表达式可以在页面顶部找到:http://emailregex.com/但使用的IP地址模式在互联网上浮动,错误是允许点分隔地址中的任何无符号字节十进制值为00,这是非法的。它的其余部分似乎与RFC 5322语法一致,并通过了几个使用grep-Po的测试,包括域名、IP地址、坏域名以及带引号和不带引号的帐户名。

纠正了IP模式中的00错误,我们获得了一个有效且相当快的正则表达式。(抓取呈现的版本,而不是markdown,以获取实际代码。)

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

or:

(?:[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])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-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])+)\])

下面是上述正则表达式的有限状态机图,它比正则表达式本身更清楚

Perl和PCRE中更复杂的模式(例如PHP中使用的正则表达式库)可以正确地解析RFC 5322而不会出现问题。Python和C#也可以做到这一点,但它们使用的语法与前两种不同。然而,如果您被迫使用许多功能较弱的模式匹配语言之一,那么最好使用真正的解析器。

同样重要的是要理解,根据RFC验证它绝对不会告诉您该地址是否确实存在于所提供的域中,或者输入该地址的人是否是其真正的所有者。人们总是以这种方式向其他人注册邮件列表。修复这一问题需要一种更高级的验证,包括向该地址发送一条消息,该消息包含一个确认令牌,该令牌与该地址在同一网页上输入。

确认令牌是知道您获得输入者地址的唯一方法。这就是为什么现在大多数邮件列表都使用该机制来确认注册。毕竟,任何人都可以放下president@whitehouse.gov,这甚至会被解析为合法,但不太可能是另一端的人。

对于PHP,您不应该使用“用PHP验证电子邮件地址”中给出的模式,我引用的正确方法是:

常见的用法和广泛的草率编码可能会为电子邮件地址建立一个事实上的标准,这比记录的正式标准更具限制性。

这并不比其他所有非RFC模式更好。它甚至不足以处理RFC 822,更不用说RFC 5322了。然而,这一个是。

如果你想变得花哨和迂腐,就实现一个完整的状态引擎。正则表达式只能充当基本过滤器。正则表达式的问题是,从用户的角度来看,告诉某人他们完全有效的电子邮件地址是无效的(假阳性),因为你的正则表达式无法处理它,这是粗鲁和不礼貌的。用于此目的的状态引擎可以验证甚至纠正电子邮件地址,否则这些地址将被视为无效,因为它根据每个RFC分解电子邮件地址。这可能会带来更愉悦的体验,比如

指定的电子邮件地址'myemail@address,com'无效。你是说myemail@address.com'?

另请参阅验证电子邮件地址,包括注释。或比较电子邮件地址验证正则表达式。

Debugex演示

其他回答

我从不麻烦用自己的正则表达式创建,因为很可能其他人已经提出了更好的版本。我总是使用regexlib来找到一个我喜欢的。

如果你想改进一个多年来运行良好的正则表达式,那么答案就取决于你到底想要实现什么——哪些类型的电子邮件地址出现了问题。微调电子邮件正则表达式非常困难,我还没有找到一个完美的解决方案。

如果您的应用程序涉及一些非常技术性的内容(或组织内部的内容),那么您可能需要支持IP地址,而不是域名,或电子邮件地址“本地”部分的注释。如果您的应用程序是跨国的,我会考虑关注Unicode和UTF-8支持。

您问题的主要答案目前链接到“完全符合RFC‑822的正则表达式”。然而,尽管该正则表达式很复杂,并且假定它在RFC规则中注重细节,但它在Unicode支持方面完全失败。

我为大多数应用程序编写的正则表达式侧重于Unicode支持,以及对RFC标准的总体遵守:

/^(?!\.)((?!.*\.{2})[a-zA-Z0-9\u0080-\u00FF\u0100-\u017F\u0180-\u024F\u0250-\u02AF\u0300-\u036F\u0370-\u03FF\u0400-\u04FF\u0500-\u052F\u0530-\u058F\u0590-\u05FF\u0600-\u06FF\u0700-\u074F\u0750-\u077F\u0780-\u07BF\u07C0-\u07FF\u0900-\u097F\u0980-\u09FF\u0A00-\u0A7F\u0A80-\u0AFF\u0B00-\u0B7F\u0B80-\u0BFF\u0C00-\u0C7F\u0C80-\u0CFF\u0D00-\u0D7F\u0D80-\u0DFF\u0E00-\u0E7F\u0E80-\u0EFF\u0F00-\u0FFF\u1000-\u109F\u10A0-\u10FF\u1100-\u11FF\u1200-\u137F\u1380-\u139F\u13A0-\u13FF\u1400-\u167F\u1680-\u169F\u16A0-\u16FF\u1700-\u171F\u1720-\u173F\u1740-\u175F\u1760-\u177F\u1780-\u17FF\u1800-\u18AF\u1900-\u194F\u1950-\u197F\u1980-\u19DF\u19E0-\u19FF\u1A00-\u1A1F\u1B00-\u1B7F\u1D00-\u1D7F\u1D80-\u1DBF\u1DC0-\u1DFF\u1E00-\u1EFF\u1F00-\u1FFFu20D0-\u20FF\u2100-\u214F\u2C00-\u2C5F\u2C60-\u2C7F\u2C80-\u2CFF\u2D00-\u2D2F\u2D30-\u2D7F\u2D80-\u2DDF\u2F00-\u2FDF\u2FF0-\u2FFF\u3040-\u309F\u30A0-\u30FF\u3100-\u312F\u3130-\u318F\u3190-\u319F\u31C0-\u31EF\u31F0-\u31FF\u3200-\u32FF\u3300-\u33FF\u3400-\u4DBF\u4DC0-\u4DFF\u4E00-\u9FFF\uA000-\uA48F\uA490-\uA4CF\uA700-\uA71F\uA800-\uA82F\uA840-\uA87F\uAC00-\uD7AF\uF900-\uFAFF\.!#$%&'*+-/=?^_`{|}~\-\d]+)@(?!\.)([a-zA-Z0-9\u0080-\u00FF\u0100-\u017F\u0180-\u024F\u0250-\u02AF\u0300-\u036F\u0370-\u03FF\u0400-\u04FF\u0500-\u052F\u0530-\u058F\u0590-\u05FF\u0600-\u06FF\u0700-\u074F\u0750-\u077F\u0780-\u07BF\u07C0-\u07FF\u0900-\u097F\u0980-\u09FF\u0A00-\u0A7F\u0A80-\u0AFF\u0B00-\u0B7F\u0B80-\u0BFF\u0C00-\u0C7F\u0C80-\u0CFF\u0D00-\u0D7F\u0D80-\u0DFF\u0E00-\u0E7F\u0E80-\u0EFF\u0F00-\u0FFF\u1000-\u109F\u10A0-\u10FF\u1100-\u11FF\u1200-\u137F\u1380-\u139F\u13A0-\u13FF\u1400-\u167F\u1680-\u169F\u16A0-\u16FF\u1700-\u171F\u1720-\u173F\u1740-\u175F\u1760-\u177F\u1780-\u17FF\u1800-\u18AF\u1900-\u194F\u1950-\u197F\u1980-\u19DF\u19E0-\u19FF\u1A00-\u1A1F\u1B00-\u1B7F\u1D00-\u1D7F\u1D80-\u1DBF\u1DC0-\u1DFF\u1E00-\u1EFF\u1F00-\u1FFF\u20D0-\u20FF\u2100-\u214F\u2C00-\u2C5F\u2C60-\u2C7F\u2C80-\u2CFF\u2D00-\u2D2F\u2D30-\u2D7F\u2D80-\u2DDF\u2F00-\u2FDF\u2FF0-\u2FFF\u3040-\u309F\u30A0-\u30FF\u3100-\u312F\u3130-\u318F\u3190-\u319F\u31C0-\u31EF\u31F0-\u31FF\u3200-\u32FF\u3300-\u33FF\u3400-\u4DBF\u4DC0-\u4DFF\u4E00-\u9FFF\uA000-\uA48F\uA490-\uA4CF\uA700-\uA71F\uA800-\uA82F\uA840-\uA87F\uAC00-\uD7AF\uF900-\uFAFF\-\.\d]+)((\.([a-zA-Z\u0080-\u00FF\u0100-\u017F\u0180-\u024F\u0250-\u02AF\u0300-\u036F\u0370-\u03FF\u0400-\u04FF\u0500-\u052F\u0530-\u058F\u0590-\u05FF\u0600-\u06FF\u0700-\u074F\u0750-\u077F\u0780-\u07BF\u07C0-\u07FF\u0900-\u097F\u0980-\u09FF\u0A00-\u0A7F\u0A80-\u0AFF\u0B00-\u0B7F\u0B80-\u0BFF\u0C00-\u0C7F\u0C80-\u0CFF\u0D00-\u0D7F\u0D80-\u0DFF\u0E00-\u0E7F\u0E80-\u0EFF\u0F00-\u0FFF\u1000-\u109F\u10A0-\u10FF\u1100-\u11FF\u1200-\u137F\u1380-\u139F\u13A0-\u13FF\u1400-\u167F\u1680-\u169F\u16A0-\u16FF\u1700-\u171F\u1720-\u173F\u1740-\u175F\u1760-\u177F\u1780-\u17FF\u1800-\u18AF\u1900-\u194F\u1950-\u197F\u1980-\u19DF\u19E0-\u19FF\u1A00-\u1A1F\u1B00-\u1B7F\u1D00-\u1D7F\u1D80-\u1DBF\u1DC0-\u1DFF\u1E00-\u1EFF\u1F00-\u1FFF\u20D0-\u20FF\u2100-\u214F\u2C00-\u2C5F\u2C60-\u2C7F\u2C80-\u2CFF\u2D00-\u2D2F\u2D30-\u2D7F\u2D80-\u2DDF\u2F00-\u2FDF\u2FF0-\u2FFF\u3040-\u309F\u30A0-\u30FF\u3100-\u312F\u3130-\u318F\u3190-\u319F\u31C0-\u31EF\u31F0-\u31FF\u3200-\u32FF\u3300-\u33FF\u3400-\u4DBF\u4DC0-\u4DFF\u4E00-\u9FFF\uA000-\uA48F\uA490-\uA4CF\uA700-\uA71F\uA800-\uA82F\uA840-\uA87F\uAC00-\uD7AF\uF900-\uFAFF]){2,63})+)$/i

我将避免复制粘贴完整的答案,所以我只将此链接到我在这里提供的类似答案:如何验证unicode电子邮件?

上面的正则表达式也有一个现场演示:http://jsfiddle.net/aossikine/qCLVH/3/

有关验证电子邮件地址的最佳正则表达式的最全面评估,请参阅此链接;“比较电子邮件地址验证正则表达式”

以下是当前的顶级表达式,以供参考:

/^([\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+\.)*[\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+@((((([a-z0-9]{1}[a-z0-9\-]{0,62}[a-z0-9]{1})|[a-z])\.)+[a-z]{2,6})|(\d{1,3}\.){3}\d{1,3}(\:\d{1,5})?)$/i

这是我做的。它不是防弹版,但它“简单”,几乎可以检查所有内容。

[\w+-]+(?:\.[\w+-]+)*@[\w+-]+(?:\.[\w+-]+)*(?:\.[a-zA-Z]{2,4})

我认为已经有了解释,因此您可以根据需要进行修改:

(e) [\w+-]+匹配a-z、a-z、_、+、-至少一次

(m) (?:\.[\w+-]+)*匹配a-z、a-z、_、+、-0或更多次,但需要以a开头。(点)

@ = @

(i) [\w+-]+匹配a-z、a-z、_、+、-至少一次

(l) (?:\.[\w+-]+)*匹配a-z、a-z、_、+、-0或更多次,但需要以a开头。(点)

(com)(?:\.[a-zA-Z]{2,4})匹配a-Z,a-Z 2到4次,从a开始。(点)

给出e(-m)@i(.l).com,其中(.m)和(.l)是可选的,但也可以重复多次。

我认为这会验证所有有效的电子邮件地址,但会阻止潜在的无效地址,而不使用在大多数情况下不需要的过于复杂的正则表达式。

请注意,这将允许+@-.com,但这是保持简单的折衷方案。

如果您可以接受空值(这不是无效的电子邮件),并且正在运行PHP 5.2+,我建议您:

static public function checkEmail($email, $ignore_empty = false) {
    if($ignore_empty && (is_null($email) || $email == ''))
        return true;
    return filter_var($email, FILTER_VALIDATE_EMAIL);
}