根据 RFC5321/RFC5322 对电子邮件地址进行正则表达式验证

Posted

技术标签:

【中文标题】根据 RFC5321/RFC5322 对电子邮件地址进行正则表达式验证【英文标题】:Regex validation of email addresses according to RFC5321/RFC5322 【发布时间】:2012-12-09 04:01:03 【问题描述】:

有人知道根据RFC5321/RFC5322 验证电子邮件地址的正则表达式吗?

由于(可嵌套的)cmets 使语法不规则,因此只应考虑没有 cmets 的地址。

当然,如果您有兴趣验证某人实际拥有的地址,那么唯一真正的验证就是向该地址发送电子邮件并检查所有者是否收到了它。然而,我纯粹对 RFC 标准感兴趣。对于实用的方法,this question 更相关。

在 cmets 之上,我愿意牺牲折叠空白,但除此之外,我对拒绝任何 RFC5321/2 有效地址的表达式不感兴趣。 (可以说,在某些情况下忽略折叠空白甚至是有意义的。)

理想情况下,正则表达式会拒绝任何 RFC 有效的内容,但这并不重要。例如,在正则表达式中包含详尽的***域列表并不是那么有趣。只需接受任何***域就足够了。

我不确定地址标签(例如 address+tag@domain.org)是否是我提到的 RFC 的一部分,但我希望正则表达式能够验证这些。

IPv6 应该被正确处理 (RFC5952)。

据我了解,国际化电子邮件(RFC6530、RFC6531、RFC6532、RFC6533)仍处于实验阶段,但验证这些地址的表达式也会很有趣。

为了使答案普遍有趣,如果任何正则表达式采用 POSIX 格式,那就太好了。

【问题讨论】:

传统的正则表达式无法做到这一点。电子邮件地址可以包含具有任意深度嵌套的 cmets,这不能被正则表达式语法解析。 @Bergi - 是的(而且非常好)。但是如果(可能是嵌套的)cmets 首先被剥离出来,那么它就可以完成。这就是 Rafał Toboła 链接到的 perl 正则表达式解决方案是如何做到的。 @Bergi - 感谢您的评论 (+1)。我已经编辑了我的问题。 【参考方案1】:

嵌套 cmets 使电子邮件地址的语法不规则(与上下文无关)。但是,如果您排除 cmets,则生成的语法是规则的。主要定义允许(折叠)词法标记之间的空格(例如a @ b.com)。删除所有折叠空格会生成规范形式。

这是根据 RFC 5322(不包括 cmets)规范电子邮件地址的正则表达式:

([!#-'*+/-9=?A-Z^-~-]+(\.[!#-'*+/-9=?A-Z^-~-]+)*|"([]!#-[^-~ \t]|(\\[\t -~]))+")@([!#-'*+/-9=?A-Z^-~-]+(\.[!#-'*+/-9=?A-Z^-~-]+)*|\[[\t -Z^-~]*])

如果您需要接受折叠空格,那么这是根据 RFC 5322(排除 cmets)的电子邮件地址的正则表达式:

((([\t ]*\r\n)?[\t ]+)?[-!#-'*+/-9=?A-Z^-~]+(\.[-!#-'*+/-9=?A-Z^-~]+)*(([\t ]*\r\n)?[\t ]+)?|(([\t ]*\r\n)?[\t ]+)?"(((([\t ]*\r\n)?[\t ]+)?([]!#-[^-~]|(\\[\t -~])))+(([\t ]*\r\n)?[\t ]+)?|(([\t ]*\r\n)?[\t ]+)?)"(([\t ]*\r\n)?[\t ]+)?)@((([\t ]*\r\n)?[\t ]+)?[-!#-'*+/-9=?A-Z^-~]+(\.[-!#-'*+/-9=?A-Z^-~]+)*(([\t ]*\r\n)?[\t ]+)?|(([\t ]*\r\n)?[\t ]+)?\[((([\t ]*\r\n)?[\t ]+)?[!-Z^-~])*(([\t ]*\r\n)?[\t ]+)?](([\t ]*\r\n)?[\t ]+)?)

RFC 5321 (SMTP) 进一步限制了有效的电子邮件地址。它基本上只留下@-sign 之前的部分,但只接受@-sign 之后的主机名或地址文字。 (“---.---”是有效的点原子,但不是有效的主机名,“[...]”是有效的域文字,但不是有效的地址文字。)

RFC 5321 中的语法在涉及主机名和 IP 地址时过于宽松。我冒昧地“纠正”了有问题的规则,使用this draft 和RFC 1034(第3.5 节)作为指导。这是生成的正则表达式。

([!#-'*+/-9=?A-Z^-~-]+(\.[!#-'*+/-9=?A-Z^-~-]+)*|"([]!#-[^-~ \t]|(\\[\t -~]))+")@([0-9A-Za-z]([0-9A-Za-z-]0,61[0-9A-Za-z])?(\.[0-9A-Za-z]([0-9A-Za-z-]0,61[0-9A-Za-z])?)*|\[((25[0-5]|2[0-4][0-9]|1[0-9]2|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9]2|[1-9]?[0-9]))3|IPv6:((((0|[1-9A-Fa-f][0-9A-Fa-f]0,3):)6|::((0|[1-9A-Fa-f][0-9A-Fa-f]0,3):)5|[0-9A-Fa-f]0,4::((0|[1-9A-Fa-f][0-9A-Fa-f]0,3):)4|(((0|[1-9A-Fa-f][0-9A-Fa-f]0,3):)?(0|[1-9A-Fa-f][0-9A-Fa-f]0,3))?::((0|[1-9A-Fa-f][0-9A-Fa-f]0,3):)3|(((0|[1-9A-Fa-f][0-9A-Fa-f]0,3):)0,2(0|[1-9A-Fa-f][0-9A-Fa-f]0,3))?::((0|[1-9A-Fa-f][0-9A-Fa-f]0,3):)2|(((0|[1-9A-Fa-f][0-9A-Fa-f]0,3):)0,3(0|[1-9A-Fa-f][0-9A-Fa-f]0,3))?::(0|[1-9A-Fa-f][0-9A-Fa-f]0,3):|(((0|[1-9A-Fa-f][0-9A-Fa-f]0,3):)0,4(0|[1-9A-Fa-f][0-9A-Fa-f]0,3))?::)((0|[1-9A-Fa-f][0-9A-Fa-f]0,3):(0|[1-9A-Fa-f][0-9A-Fa-f]0,3)|(25[0-5]|2[0-4][0-9]|1[0-9]2|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9]2|[1-9]?[0-9]))3)|(((0|[1-9A-Fa-f][0-9A-Fa-f]0,3):)0,5(0|[1-9A-Fa-f][0-9A-Fa-f]0,3))?::(0|[1-9A-Fa-f][0-9A-Fa-f]0,3)|(((0|[1-9A-Fa-f][0-9A-Fa-f]0,3):)0,6(0|[1-9A-Fa-f][0-9A-Fa-f]0,3))?::)|(?!IPv6:)[0-9A-Za-z-]*[0-9A-Za-z]:[!-Z^-~]+)])

所有正则表达式都是 POSIX ERE。最后一个使用负前瞻。正则表达式的推导参见here。

【讨论】:

这个正则表达式对 rfc6532 没有任何抱怨,因为它将联系部分限制为 ascii。 @MihailKrivushin 完全同意。问题是关于 RFC5321/2 的具体问题...... 为什么第一个正则表达式的字符组中没有a-z^-~ 包含哪些字符?需要这个范围吗? @mxmlnkn a-z 范围包含在 ^-~ 中。如果您搜索 ASCII 表,您可以看到范围中包含哪些字符。 这会从 eslint 引发空字符类警告 - eslint.org/docs/rules/no-empty-character-class【参考方案2】:

更新:

Michael Stramel 指出 RFC822 已过时,请参阅他的评论。


据我所知,RFC822 指定了电子邮件地址语法。

http://www.ex-parrot.com/pdw/Mail-RFC822-Address.html

【讨论】:

谢谢。我知道这个正则表达式,但我对 RFC5321/2 感兴趣。 RFC822 已经过时,应该改用 RFC5322。 en.wikipedia.org/wiki/List_of_RFCs

以上是关于根据 RFC5321/RFC5322 对电子邮件地址进行正则表达式验证的主要内容,如果未能解决你的问题,请参考以下文章

Rails 6- 符合 RFC5322 的电子邮件验证

正则表达式,获取电子邮件日期头字段的所有部分

符合RFC的电子邮件地址验证程序

ruby 符合RFC 5322的消息ID生成器

从历史上看,为啥关于电子邮件地址的 RFC 变得如此复杂? [关闭]

带有特殊字符的电子邮件被拒绝 - RFC-6532 和“quoted-printable”