多年来,我慢慢地开发了一个正则表达式,它可以正确验证大多数电子邮件地址,假设它们不使用IP地址作为服务器部分。
我在几个PHP程序中使用它,而且大多数时候都可以使用。然而,不时有人与我联系,他们对使用它的网站有问题,我最终不得不做出一些调整(最近我意识到我不允许四个字符的TLD)。
你有或见过验证电子邮件的最佳正则表达式是什么?
我见过几种使用函数的解决方案,这些函数使用了几个较短的表达式,但我宁愿在简单函数中使用一个长的复杂表达式,而不是在更复杂的函数中使用几个短表达式。
多年来,我慢慢地开发了一个正则表达式,它可以正确验证大多数电子邮件地址,假设它们不使用IP地址作为服务器部分。
我在几个PHP程序中使用它,而且大多数时候都可以使用。然而,不时有人与我联系,他们对使用它的网站有问题,我最终不得不做出一些调整(最近我意识到我不允许四个字符的TLD)。
你有或见过验证电子邮件的最佳正则表达式是什么?
我见过几种使用函数的解决方案,这些函数使用了几个较短的表达式,但我宁愿在简单函数中使用一个长的复杂表达式,而不是在更复杂的函数中使用几个短表达式。
当前回答
我用这个;
^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$
其他回答
我们还有另一个选择,就是使用具有EmailAddressAttribute的DataAnnotations。这不仅可以应用于类的属性,还可以在运行时加以利用。
使用System.ComponentModel.DataAnnotations;
典型用途
public class Person
{
public int Id { get; set; }
[EmailAddress]
public string Email { get; set; }
}
运行时
var emailAddressAttribute = new EmailAddressAttribute();
if (emailAddressAttribute.IsValid("name@email.com"))
{
//email is valid
}
else
{
//email is invalid
}
如前所述,您不能使用正则表达式验证电子邮件。然而,这是我们目前用来确保用户输入不是完全虚假的(忘记TLD等)。
此正则表达式将允许IDN域和@符号前后的特殊字符(如Umlauts)。
/^[\w.+-_]+@[^.][\w.-]*\.[\w-]{2,63}$/iu
根据我所看到的,一个完全符合标准的正则表达式是允许的:
/^(?!(^[.-].*|.*[.-]@|.*\.{2,}.*)|^.{254}.+@)([a-z\xC0-\xFF0-9!#$%&'*+\/=?^_`{|}~.-]+@)(?!.{253}.+$)((?!-.*|.*-\.)([a-z0-9-]{1,63}\.)+[a-z]{2,63}|(([01]?[0-9]{2}|2([0-4][0-9]|5[0-5])|[0-9])\.){3}([01]?[0-9]{2}|2([0-4][0-9]|5[0-5])|[0-9]))$/gim
演示/调试分析(交互式)
拆分:
^(?!(^[.-].*|.*[.-]@|.*\.{2,}.*)|^.{254}.+@)
([a-z\xC0-\xFF0-9!#$%&'*+\/=?^_`{|}~.-]+@)
(?!.{253}.+$)
(
(?!-.*|.*-\.)
([a-z0-9-]{1,63}\.)+
[a-z]{2,63}
|
(([01]?[0-9]{2}|2([0-4][0-9]|5[0-5])|[0-9])\.){3}
([01]?[0-9]{2}|2([0-4][0-9]|5[0-5])|[0-9])
)$
分析:
(?!(^[.-].*|.*[.-]@|.*\.{2,}.*)|^.{254}.+@)
对以。,以一结尾,有。。或超过254个字符的最大长度
([a-z\xC0-\xFF0-9!#$%&'*+\/=?^_`{|}~.-]+@)
匹配一个或多个允许的字符,并应用负面外观
(?!.{253}.+$)
域名部分的负前瞻性,总共限制为253个字符
(?!-.*|.*-\.)
每个域名的负前瞻性,不允许以开头或结尾。
([a-z0-9-]{1,63}\.)+
域名中允许的字符的简单组匹配,每个字符限制为63个字符
[a-zA-Z]{2,63}
允许的顶级域的简单组匹配,该域目前仍仅限于字母,但确实包含4个字母以上的TLD。
(([01]?[0-9]{2}|2([0-4][0-9]|5[0-5])|[0-9])\.){3}
([01]?[0-9]{2}|2([0-4][0-9]|5[0-5])|[0-9])
域名的替代方案:这将IP地址中的前3个数字与匹配。然后是IP地址中没有的第四个数字。在它背后。
我也有类似的愿望:希望快速检查电子邮件地址中的语法,而不要过分使用电子邮件发送实用程序(邮件::RFC822::地址答案显然是正确的答案)。我同意这个(我是一个POSIX正则表达式的人,所以我通常不会使用PCRE中的\d等,因为它们让我看不清楚):
preg_match("_^[-!#-'*+/-9=?A-Z^-~]+(\.[-!#-'*+/-9=?A-Z^-~]+)*@[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])?)*\$_", $adr)
这是RFC正确的,但它明确排除了过时的表单以及直接IP地址(IP地址和传统IP地址),而该实用程序的目标组中的某些人(主要是:在IRC上的#sendmail中打扰我们的人)通常不希望或不需要这些地址。
IDN(国际化域名)明确不在电子邮件范围内:地址如“foo@cäcilenchor bonn.de“必须写”foo@xn--ccilienchor-bonn-vnb.de而在网络上(这包括HTML中的mailto:links和这样的乐趣),只允许GUI向用户显示(并接受然后转换)这样的名称。
电子邮件地址的正则表达式为:
/^("(?:[!#-\[\]-\u{10FFFF}]|\\[\t -\u{10FFFF}])*"|[!#-'*+\-/-9=?A-Z\^-\u{10FFFF}](?:\.?[!#-'*+\-/-9=?A-Z\^-\u{10FFFF}])*)@([!#-'*+\-/-9=?A-Z\^-\u{10FFFF}](?:\.?[!#-'*+\-/-9=?A-Z\^-\u{10FFFF}])*|\[[!-Z\^-\u{10FFFF}]*\])$/u
此正则表达式与RFC 5321、RFC 5322和RFC 6532中指定的非过时电子邮件地址的地址规范ABNF 100%相同。
此外,您必须验证:
电子邮件地址格式为UTF-8(或ASCII,如果无法发送到国际化电子邮件地址)地址不超过320个UTF-8字节用户部分(第一个匹配组)不超过64个UTF-8字节域部分(第二个匹配组)不超过255个UTF-8字节
完成所有这些的最简单方法是使用现有函数。在PHP中,请使用filter_VALIDATE_EMAIL和filter_FLAG_EMAIL_UNICODE(如果可以发送到国际化电子邮件地址)查看filter_var函数:
$email_valid = filter_var($email_input, FILTER_VALIDATE_EMAIL, FILTER_FLAG_EMAIL_UNICODE);
然而,也许您正在构建这样一个函数,实际上实现这一点的最简单方法是使用正则表达式。
记住,这只验证电子邮件地址不会导致语法错误。验证地址是否可以接收电子邮件的唯一方法是实际发送电子邮件。
接下来,我将讨论如何生成此正则表达式。
我写了一个新的答案,因为这里的大多数答案都犯了这样一个错误:要么指定了一个限制性太强的模式(因此没有很好地老化);或者它们呈现一个正则表达式,该表达式实际上与MIME消息的标头匹配,而不是电子邮件地址本身。
只要没有递归部分,从ABNF生成正则表达式是完全可能的。
RFC 5322规定了在MIME消息中发送什么是合法的;将此视为合法电子邮件地址的上限。
然而,完全遵循ABNF将是一个错误:这种模式在技术上表示了如何在MIME消息中编码电子邮件地址,并允许字符串不属于电子邮件地址,如折叠空格和注释;它还支持不合法生成的过时表单(但服务器出于历史原因读取)。电子邮件地址不包括这些。
RFC 5322解释了:
原子和点原子都被解释为单个单元,包括组成它的字符串。语义上,可选其余角色周围的评论和FWS不属于原子;原子只是一个原子中的一行文本字符,或点原子中的atext和“.”字符。
在某些定义中,将有非终端的名称以“obs-”开头。这些“obs-”元素是指在第4节中过时的语法。在所有情况下,这些产品为了生成合法的互联网信息,不得用作此类信息的一部分。
如果您从RFC 5322中的addr规范中删除CFWS、BWS和obs-*规则,并对结果执行一些优化(我使用了“green”),则可以生成此正则表达式,用斜线引用并锚定(适用于ECMAScript和兼容方言,为清晰起见,添加了换行符):
/^("(?:[!#-\[\]-~]|\\[\t -~])*"|[!#-'*+\-/-9=?A-Z\^-~](?:\.?[!#-'*+\-/-9=?A-Z\^-~])*)
@([!#-'*+\-/-9=?A-Z\^-~](?:\.?[!#-'*+\-/-9=?A-Z\^-~])*|\[[!-Z\^-~]*\])$/
这只支持ASCII电子邮件地址。要支持RFC 6532国际化电子邮件地址,请将~字符替换为\u{10FFFF}(PHP,带u标志的ECMAScript)或\uFFFF(用于UTF-16实现,如.NET和旧版ECMAScript/JavaScript):
/^("(?:[!#-\[\]-\u{10FFFF}]|\\[\t -\u{10FFFF}])*"|[!#-'*+\-/-9=?A-Z\^-\u{10FFFF}](?:\.?[!#-'*+\-/-9=?A-Z\^-\u{10FFFF}])*)@([!#-'*+\-/-9=?A-Z\^-\u{10FFFF}](?:\.?[!#-'*+\-/-9=?A-Z\^-\u{10FFFF}])*|\[[!-Z\^-\u{10FFFF}]*\])$/u
这是有效的,因为我们使用的ABNF不是递归的,因此形成了可以转换为正则表达式的非递归正则语法。
它是这样分解的:
用户部分(@之前)可以是点原子或带引号的字符串“([!#-\[\]-~]|\\[\t-~])*”指定用户的引号字符串形式,例如root@home“@example.com。它允许双引号内的任何非控制字符;但空格、制表符、双引号和反斜杠必须用反斜杠转义。[!#-'*+\-/-9=?A-Z\^-~]是用户点原子的第一个字符。(\.?[!#-'*+\-/-9=?A-Z\^-~])*与点原子的其余部分匹配,允许点(除了在另一个点之后或作为最终字符)。@表示域。域部分可以是点原子或域文字。[!#-'*+\-/-9=?A-Z\^-~](\.?[!#-'*+\-/-9=?A-Z \^-~])*与上面的点原子形式相同,但这里它表示域名和IPv4地址。\[[!-Z\^-~]*\]将匹配IPv6地址和主机名的未来定义。
此正则表达式允许所有符合规范的电子邮件地址,并且可以在MIME消息中逐字使用(除了行长度限制,在这种情况下必须添加折叠空格)。
这还设置了非捕获组,使得match[1]将是用户,match[2]将是主机。(但是,如果匹配项[1]以双引号开头,则过滤掉反斜杠转义符以及开头和结尾双引号:“root”@example.com和root@example.com识别同一收件箱。)
最后,请注意,RFC 5321对电子邮件地址的长度设置了限制。用户部分最多可为64字节,域部分最多为255字节。包括@字符在内,整个地址的限制为320字节。地址是UTF-8编码后的字节数;而不是字符。
注意,RFC 5322 ABNF为域名定义了一个允许的语法,允许当前已知的名称无效。这也允许域名将来成为合法的。这不应该是一个问题,因为这应该以与不存在的域名相同的方式处理。
始终考虑用户键入的电子邮件地址有效,但他们无法访问的可能性。验证电子邮件地址的唯一简单方法是发送电子邮件。
这是改编自我的文章电子邮件地址和语法。