验证电子邮件地址

29

我正在尝试使用以下代码使用C#发送电子邮件。

MailMessage mail = new MailMessage();
mail.From = new MailAddress(fromAddress, friendlyName);
mail.To.Add(toAddress);
mail.CC.Add(ccAddress);

//set the content
mail.Subject = emailSubject;
mail.Body = emailHeader + "\n" + emailBody;

//send the message
SmtpClient smtp = new SmtpClient(ServerAddress);
smtp.Credentials = CredentialCache.DefaultNetworkCredentials;
mail.IsBodyHtml = true;
smtp.Send(mail);
现在我的函数接收到的"toAddress"字符串可能包含单个地址,也可能有多个逗号分隔的地址。
问题是,在多个逗号分隔的地址中,其中一个或两个可能格式错误。
因此,当我尝试使用此代码发送电子邮件时,会出现异常:"指定的字符串不符合电子邮件地址所需的格式。"
是否有任何方法可以验证逗号分隔的电子邮件地址?我曾经在某处读到过,验证电子邮件地址的唯一方法是向它发送电子邮件,因为验证电子邮件地址的正则表达式可能非常庞大。
另外,我无法控制设计或该地址字符串如何到达我的函数,我无法在UI中添加电子邮件验证,所以我很无助...
我的问题是,即使只有一些地址格式错误,电子邮件也将无法传送到逗号分隔字符串中的所有地址。
是否有任何方法可以在.NET中正确验证电子邮件地址?是否有一种方法可以筛选出错误的电子邮件地址并仅将邮件发送给好的地址?

我有点困惑。你是想验证电子邮件地址还是只是电子邮件地址的格式? - beyerss
13个回答

31

这是我们在生产环境中使用的代码(我还为您添加了一个逗号)。通常情况下,你不应该使用 try/catch 进行验证,但是在这里它表现良好。我认为这比试图重新编写验证器要好。

string[] allToAddresses = to.Split(";,".ToCharArray(),
                                 StringSplitOptions.RemoveEmptyEntries)
foreach (string toAddress in allToAddresses)
{
    try
    {
        message.To.Add(toAddress);
    }
    catch (FormatException)
    {
        //do nothing, ill-formed address. 
    }
}

4
你确定Add方法抛出的每个异常都意味着错误的地址吗?你只应该捕获那些确实表示坏地址的异常。你的代码将阻止你看到那个表明“某些事情非常错误,我希望有人能看到并采取行动,我现在要死了,再见”的异常。 - John Saunders
2
我知道这不会受欢迎 - 但是是的。请查看http://msdn.microsoft.com/en-us/library/ms144695.aspx - 有两个关于null和empty的异常,还有一个FormatException。我猜你总是可以得到一个奇怪的异常(比如内存不足之类的),所以我会进行更改。谢谢。 - Kobi
3
此外,.NET并非Java,Microsoft可以随着时间的推移添加异常。失败捕获某些实际上意味着无效电子邮件地址的异常要比捕获并忽略一些暗示出现严重问题的异常更好。 - John Saunders
地址错误。算了吧。 的确,干得好先生。我赞同你的“这个异常无关紧要”的评论。 - JJS
1
@Quarkly - 相反的是,这是.NET中唯一承诺电子邮件有效的方法,您编写的任何正则表达式都不会与此验证相同(因为最终.NET会执行它)。 "RegEx exception"可能是在瞬间写下的,但是特别针对正则表达式,请查看我的个人资料-大多数人认为我在这方面成长得太快了。请善待您的QA。 - Kobi

24

目前我们正在使用以下函数,它对我们来说运行得非常好 :)

public static bool IsValidEmail(string email)
{
    // source: http://thedailywtf.com/Articles/Validating_Email_Addresses.aspx
    Regex rx = new Regex(
    @"^[-!#$%&'*+/0-9=?A-Z^_a-z{|}~](\.?[-!#$%&'*+/0-9=?A-Z^_a-z{|}~])*@[a-zA-Z](-?[a-zA-Z0-9])*(\.[a-zA-Z](-?[a-zA-Z0-9])*)+$");
    return rx.IsMatch(email);
}
请使用以下代码:
(?:[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])+)\])

4
那个验证了aman@village.i,但是没有一个字母的顶级域名。 - Jonas Elfström
1
展示你的工作会得到加分,不过我只需要改变正则表达式就行了。 - Narnian
在这个答案中,第一个是我迄今为止找到的最好的...尽管不能正确验证IPv6或引号内的特殊字符。不幸的是,第二个不好。 - beyond-code
这是在Regexr上的,看起来非常接近防弹?https://regexr.com/48vms - Vincent Buscarello
电子邮件@123.123.123.123无法通过。 - Suraj

24

你可以通过逗号将电子邮件字符串拆分,并使用简单(或巨大的)电子邮件正则表达式验证每个电子邮件地址。或者,尝试创建一个 MailAddress 对象;它也支持一些基本的地址验证。


1
你的回复比我快几秒,所以我没有重新发布它...+1 - AlexDrenea
2
+1. 我认为MailAddress构造函数对他目前的需求已经足够了(在第一次发送之前进行快速检查)。 - Matthew Flaschen
1
你还应该注意到,电子邮件通常使用分号而不是逗号作为分隔符。 - Kobi
1
Kobi,以前可以用...现在只接受逗号。http://rstew.blogspot.com/2007/06/specified-string-is-not-in-form.html - ashwnacharya
2
FYI,使用MailAddress验证电子邮件地址并不是绝对可靠的。它似乎允许一些不应该通过的错误地址,比如"bob.@mysite.com"或"bob..smith@mysite.com"。 - Jacobs Data Solutions
显示剩余3条评论

20

要做到100%符合RFC的电子邮件验证很难,有关详细信息,请参见此答案。 在.NET中,使用两种相对简单的方法之一是使用MailAddress

try {
    new MailAddress("invalid_email");
} catch (FormatException) {
    // invalid
}
// TODO: ask MS to implement MailAddress.TryParse to avoid exception

或者使用来自MSDN的更严格基于正则表达式的方法(可处理IDN和.NET 4.5的正则表达式解析超时):

// NET 4.0
Boolean mappingInvalid = false;
emailString = Regex.Replace(emailString, @"(@)(.+)$", match => {
    String domainName = match.Groups[2].Value;
    try {
        domainName = new IdnMapping().GetAscii(domainName);
    } catch (ArgumentException) {
        mappingInvalid = true;
    }
    return match.Groups[1].Value + domainName;
});
if (mappingInvalid) {
    return false;
}
return Regex.IsMatch(emailString,
        @"^(?("")(""[^""]+?""@)|(([0-9a-z]((\.(?!\.))|[-!#\$%&'\*\+/=\?\^`\{\}\|~\w])*)(?<=[0-9a-z])@))" +
        @"(?(\[)(\[(\d{1,3}\.){3}\d{1,3}\])|(([0-9a-z][-\w]*[0-9a-z]*\.)+[a-z0-9]{2,17}))$",
        RegexOptions.IgnoreCase);

2
第一种解决方案很简单,依赖于所有.NET程序员都可以访问的(可能)经过非常充分测试的代码。做得好! - Liedman
有时捕获特定异常是处理条件的唯一方法。有些情况下,库设计者犯了错误,没有让你主动检查条件。例如,没有MailAddress.TryParse方法可以避免异常。Eric Lippert将这些分类为“令人烦恼”的异常 https://ericlippert.com/2008/09/10/vexing-exceptions/ 更多信息请参见:https://dev59.com/rGw05IYBdhLWcg3wmCwo#7152374。 - Dmitry
从.NET 5开始,MailAddress.TryCreate()在第一个解决方案中运行良好。 - Mark Willis
请注意,new MailAddress("test 123@test.com") 是有效的,但会创建带有“test”作为显示名称的地址 123@test,com ("test" <123@test.com>)。因此,您可能还需要检查空格。 不过,这仍然比EmailAddressAttribute更好,因为它可以捕获域名中的逗号,这是常见的拼写错误。 - Oliver Schimmer

6
using System.ComponentModel.DataAnnotations;

return new EmailAddressAttribute().IsValid(strIn);

不幸的是,与 new MailAddress("...") 不同,这允许出现 test@test,com(逗号而不是点),这是我们每周都会遇到的一个非常常见的错误。 - Oliver Schimmer

5
以下内容将检查电子邮件地址是否符合正确格式(而不是实际存在):
private bool isEmail(string inputEmail)
{
    Regex re = new Regex(@"^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$",
                  RegexOptions.IgnoreCase);
    return re.IsMatch(inputEmail);
}

我已更新此内容,以更简单的表达方式(包括不区分大小写)来使其更加清晰易懂。

以下是验证域名是否实际存在的基本代码:

private bool isRealDomain(string inputEmail)
{
    bool isReal = false;
    try
    {
        string[] host = (inputEmail.Split('@'));
        string hostname = host[1];

        IPHostEntry IPhst = Dns.GetHostEntry(hostname);
        IPEndPoint endPt = new IPEndPoint(IPhst.AddressList[0], 25);
        Socket s = new Socket(endPt.AddressFamily,
                SocketType.Stream, ProtocolType.Tcp);
        s.Connect(endPt);
        s.Close();
        isReal = true;
    }
    catch (<specific exceptions here>)
    {
    }

    return isReal;
}

实际上,您可以做更多的事情,例如尝试连接以验证域是否会收到邮件。此外,您需要确保捕获必要的异常并正确处理它们。


在我看来,你在“try”语句块中有太多的语句,而你却用“catch”忽略了所有的异常。其中任何一个语句都可能因与电子邮件地址的有效性无关的原因而失败,而你将不会知道它。 - John Saunders
@jonelf - 我必须承认我“借用”了这个正则表达式,还没有时间仔细检查它。感谢你的提醒。 - ChrisF
@John Saunders - 这段代码只是作为一个示例存在。如果你觉得它妨碍了,我可以删除try...catch部分。 - ChrisF
@ChrisF:相反,请考虑树立一个良好的榜样。你知道有多少人会找到这些答案并复制粘贴代码。你知道这只是一个例子,我知道这只是一个例子,但他们中的许多人在复制和粘贴之前不阅读。 - John Saunders
@jonelf - 我已经更新了正则表达式,现在可以处理电子邮件中的“+”符号。再次测试时还显示了一些后续代码中的错误,所以再次感谢您指出这一点。 - ChrisF
显示剩余4条评论

3
微软刚刚更新了他们关于验证电子邮件的文档,它非常好用。链接: https://learn.microsoft.com/en-us/dotnet/standard/base-types/how-to-verify-that-strings-are-in-valid-email-format
代码片段:
using System;
using System.Globalization;
using System.Text.RegularExpressions;

public class RegexUtilities
{
   bool invalid = false;

   public bool IsValidEmail(string strIn)
   {
       invalid = false;
       if (String.IsNullOrEmpty(strIn))
          return false;

       // Use IdnMapping class to convert Unicode domain names.
       try {
          strIn = Regex.Replace(strIn, @"(@)(.+)$", this.DomainMapper,
                                RegexOptions.None, TimeSpan.FromMilliseconds(200));
       }
       catch (RegexMatchTimeoutException) {
         return false;
       }

        if (invalid)
           return false;

       // Return true if strIn is in valid email format.
       try {
          return Regex.IsMatch(strIn,
                @"^(?("")("".+?(?<!\\)""@)|(([0-9a-z]((\.(?!\.))|[-!#\$%&'\*\+/=\?\^`\{\}\|~\w])*)(?<=[0-9a-z])@))" +
                @"(?(\[)(\[(\d{1,3}\.){3}\d{1,3}\])|(([0-9a-z][-0-9a-z]*[0-9a-z]*\.)+[a-z0-9][\-a-z0-9]{0,22}[a-z0-9]))$",
                RegexOptions.IgnoreCase, TimeSpan.FromMilliseconds(250));
       }
       catch (RegexMatchTimeoutException) {
          return false;
       }
   }

   private string DomainMapper(Match match)
   {
      // IdnMapping class with default property values.
      IdnMapping idn = new IdnMapping();

      string domainName = match.Groups[2].Value;
      try {
         domainName = idn.GetAscii(domainName);
      }
      catch (ArgumentException) {
         invalid = true;
      }
      return match.Groups[1].Value + domainName;
   }
}

2

因为其他答案似乎都没有展示一个100%工作的正则表达式,我来试一下。这个正则表达式是从生产代码中提取的,不会受到单字母顶级域名问题的影响(me@mydomain.x)。

private bool IsEmailSyntaxValid(string emailToValidate)
{
    return System.Text.RegularExpressions.Regex.IsMatch(emailToValidate,
        @"^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$");
}

我不记得这是从哪里得到的,但就像我说的,在生产中它对我很有效。


1
抱歉,给这个点踩了。-- "info@about.museum" 不符合验证要求。 - Warren Rumak
啊,没问题。我没有看到那个。 - Ryan O'Neill

2

目前似乎没有很好的方法来验证电子邮件地址。

到目前为止,我还没有找到一个能正确验证的正则表达式。使用正则表达式会导致某些电子邮件地址被阻止。因此,我们的重点是确保在发送时MailMessage不会抛出异常。如果有异常,我们可以通知用户。但除此之外,如果您提供了错误的电子邮件地址,您将无法收到邮件。


1
你可以随时在此网站使用正则表达式,它非常庞大且完全不可读,但它可以解决大多数边缘情况。
(?:(?:\r\n)?[ \t])*(?:(?:(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t]
)+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:
\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(
?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ 
\t]))*"(?:(?:\r\n)?[ \t])*))*@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\0
31]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\
](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+
(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:
(?:\r\n)?[ \t])*))*|(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z
|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)
?[ \t])*)*\<(?:(?:\r\n)?[ \t])*(?:@(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\
r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[
 \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)
?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t]
)*))*(?:,@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[
 \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*
)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t]
)+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*)
*:(?:(?:\r\n)?[ \t])*)?(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+
|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r
\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:
\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t
]))*"(?:(?:\r\n)?[ \t])*))*@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031
]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](
?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?
:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?
:\r\n)?[ \t])*))*\>(?:(?:\r\n)?[ \t])*)|(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?
:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?
[ \t]))*"(?:(?:\r\n)?[ \t])*)*:(?:(?:\r\n)?[ \t])*(?:(?:(?:[^()<>@,;:\\".\[\] 
\000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|
\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>
@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"
(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*))*@(?:(?:\r\n)?[ \t]
)*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\
".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?
:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[
\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*|(?:[^()<>@,;:\\".\[\] \000-
\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(
?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)*\<(?:(?:\r\n)?[ \t])*(?:@(?:[^()<>@,;
:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([
^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\"
.\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\
]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*(?:,@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\
[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\
r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] 
\000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]
|\\.)*\](?:(?:\r\n)?[ \t])*))*)*:(?:(?:\r\n)?[ \t])*)?(?:[^()<>@,;:\\".\[\] \0
00-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\
.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,
;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?
:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*))*@(?:(?:\r\n)?[ \t])*
(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".
\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[
^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]
]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*\>(?:(?:\r\n)?[ \t])*)(?:,\s*(
?:(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\
".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)(?:\.(?:(
?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[
\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t
])*))*@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t
])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?
:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|
\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*|(?:
[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\
]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)*\<(?:(?:\r\n)
?[ \t])*(?:@(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["
()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)
?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>
@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*(?:,@(?:(?:\r\n)?[
 \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,
;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t]
)*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\
".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*)*:(?:(?:\r\n)?[ \t])*)?
(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".
\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)(?:\.(?:(?:
\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\[
"()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])
*))*@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])
+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\
.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z
|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*\>(?:(
?:\r\n)?[ \t])*))*)?;\s*)

1
这是最好的 - 特别是我喜欢它大部分时间都能做对 - Muleskinner

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接