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

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

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

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


当前回答

我们还有另一个选择,就是使用具有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
}

其他回答

互联网上有很多这样的例子(我认为甚至有一个完全验证RFC的例子——但如果内存可用的话,它有几十/几百行)。

人们倾向于对这类事情进行验证。为什么不检查它是否有@和至少一个。并且满足一些简单的最小长度?输入一封假电子邮件并仍然匹配任何有效的正则表达式是很简单的。我猜假阳性比假阴性好。

我使用的正则表达式:

[\w-+]+([.][\w]+)?@[\w-+]+([.][a-z]{2,})+

完全符合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演示

我总是使用下面的正则表达式来验证电子邮件地址。它涵盖了基于英语字符的所有电子邮件地址格式。

"\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";

下面是一个C#示例:

添加程序集引用:

using System.Text.RegularExpressions;

并使用下面的方法传递电子邮件地址并得到一个布尔值

private bool IsValidEmail(string email) {
    bool isValid = false;
    const string pattern = @"\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";

    isValid = email != "" && Regex.IsMatch(email, pattern);

    // Same above approach in multiple lines
    //
    //if (!email) {
    //    isValid = false;
    //} else {
    //    // email param contains a value; Pass it to the isMatch method
    //    isValid = Regex.IsMatch(email, pattern);
    //}
    return isValid;
}

此方法验证传入参数的电子邮件字符串。对于param为null、空字符串、未定义或param值不是有效电子邮件地址的所有情况,它都将返回false。只有当参数包含有效的电子邮件地址字符串时,它才会返回true。

奇怪的是,您“不能”使用4个字符的TLD。您禁止人们使用.info和.name,以及长度限制stop、travel和.museum,但是的,它们比2个字符的TLD和3个字符的TLC更常见。

你也应该允许大写字母。电子邮件系统将规范本地部分和域部分。

对于域部分的正则表达式,域名不能以'-'开头,也不能以'-'结尾。Dash只能介于两者之间。

如果您使用PEAR库,请查看他们的邮件功能(我忘记了确切的名称/库)。您可以通过调用一个函数来验证电子邮件地址,它根据RFC 822中的定义验证电子邮件地址。