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

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

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

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


当前回答

RFC 5322标准:

允许点原子本地部分、引号字符串本地部分、过时(混合点原子和引号字符串)本地部分、域名域、(IPv4、IPv6和IPv4映射的IPv6地址)域文本域和(嵌套)CFWS。

'/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD'

RFC 5321标准:

允许点原子本地部分、引号字符串本地部分、域名域和(IPv4、IPv6和IPv4映射的IPv6地址)域文本域。

'/^(?!(?>"?(?>\\\[ -~]|[^"])"?){255,})(?!"?(?>\\\[ -~]|[^"]){65,}"?@)(?>([!#-\'*+\/-9=?^-~-]+)(?>\.(?1))*|"(?>[ !#-\[\]-~]|\\\[ -~])*")@(?!.*[^.]{64,})(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>\.(?2)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?3)){7}|(?!(?:.*[a-f0-9][:\]]){8,})((?3)(?>:(?3)){0,6})?::(?4)?))|(?>(?>IPv6:(?>(?3)(?>:(?3)){5}:|(?!(?:.*[a-f0-9]:){6,})(?5)?::(?>((?3)(?>:(?3)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?>\.(?6)){3}))\])$/iD'

基本:

允许点原子本地部分和域名域(需要至少两个域名标签,TLD限制为2-6个字母字符)。

"/^(?!.{255,})(?!.{65,}@)([!#-'*+\/-9=?^-~-]+)(?>\.(?1))*@(?!.*[^.]{64,})(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?\.){1,126}[a-z]{2,6}$/iD"

其他回答

在Perl 5.10或更高版本中很容易:

/(?(DEFINE)
   (?<address>         (?&mailbox) | (?&group))
   (?<mailbox>         (?&name_addr) | (?&addr_spec))
   (?<name_addr>       (?&display_name)? (?&angle_addr))
   (?<angle_addr>      (?&CFWS)? < (?&addr_spec) > (?&CFWS)?)
   (?<group>           (?&display_name) : (?:(?&mailbox_list) | (?&CFWS))? ;
                                          (?&CFWS)?)
   (?<display_name>    (?&phrase))
   (?<mailbox_list>    (?&mailbox) (?: , (?&mailbox))*)

   (?<addr_spec>       (?&local_part) \@ (?&domain))
   (?<local_part>      (?&dot_atom) | (?&quoted_string))
   (?<domain>          (?&dot_atom) | (?&domain_literal))
   (?<domain_literal>  (?&CFWS)? \[ (?: (?&FWS)? (?&dcontent))* (?&FWS)?
                                 \] (?&CFWS)?)
   (?<dcontent>        (?&dtext) | (?&quoted_pair))
   (?<dtext>           (?&NO_WS_CTL) | [\x21-\x5a\x5e-\x7e])

   (?<atext>           (?&ALPHA) | (?&DIGIT) | [!#\$%&'*+-/=?^_`{|}~])
   (?<atom>            (?&CFWS)? (?&atext)+ (?&CFWS)?)
   (?<dot_atom>        (?&CFWS)? (?&dot_atom_text) (?&CFWS)?)
   (?<dot_atom_text>   (?&atext)+ (?: \. (?&atext)+)*)

   (?<text>            [\x01-\x09\x0b\x0c\x0e-\x7f])
   (?<quoted_pair>     \\ (?&text))

   (?<qtext>           (?&NO_WS_CTL) | [\x21\x23-\x5b\x5d-\x7e])
   (?<qcontent>        (?&qtext) | (?&quoted_pair))
   (?<quoted_string>   (?&CFWS)? (?&DQUOTE) (?:(?&FWS)? (?&qcontent))*
                        (?&FWS)? (?&DQUOTE) (?&CFWS)?)

   (?<word>            (?&atom) | (?&quoted_string))
   (?<phrase>          (?&word)+)

   # Folding white space
   (?<FWS>             (?: (?&WSP)* (?&CRLF))? (?&WSP)+)
   (?<ctext>           (?&NO_WS_CTL) | [\x21-\x27\x2a-\x5b\x5d-\x7e])
   (?<ccontent>        (?&ctext) | (?&quoted_pair) | (?&comment))
   (?<comment>         \( (?: (?&FWS)? (?&ccontent))* (?&FWS)? \) )
   (?<CFWS>            (?: (?&FWS)? (?&comment))*
                       (?: (?:(?&FWS)? (?&comment)) | (?&FWS)))

   # No whitespace control
   (?<NO_WS_CTL>       [\x01-\x08\x0b\x0c\x0e-\x1f\x7f])

   (?<ALPHA>           [A-Za-z])
   (?<DIGIT>           [0-9])
   (?<CRLF>            \x0d \x0a)
   (?<DQUOTE>          ")
   (?<WSP>             [\x20\x09])
 )

 (?&address)/x

对于PHP,我使用Nette框架中的电子邮件地址验证器:

/* public static */ function isEmail($value)
{
    $atom = "[-a-z0-9!#$%&'*+/=?^_`{|}~]"; // RFC 5322 unquoted characters in local-part
    $localPart = "(?:\"(?:[ !\\x23-\\x5B\\x5D-\\x7E]*|\\\\[ -~])+\"|$atom+(?:\\.$atom+)*)"; // Quoted or unquoted
    $alpha = "a-z\x80-\xFF"; // Superset of IDN
    $domain = "[0-9$alpha](?:[-0-9$alpha]{0,61}[0-9$alpha])?"; // RFC 1034 one domain component
    $topDomain = "[$alpha](?:[-0-9$alpha]{0,17}[$alpha])?";
    return (bool) preg_match("(^$localPart@(?:$domain\\.)+$topDomain\\z)i", $value);
}

我们使用了http://www.aspnetmx.com/几年来取得了一定的成功。您可以选择要验证的级别(例如语法检查、域检查、MX记录或实际电子邮件)。

对于前端表单,我们通常会验证域是否存在以及语法是否正确,然后在进行批量邮件发送之前,我们会进行更严格的验证以清理数据库。

自2010年5月以来,互联网上存在非拉丁语(中文、阿拉伯文、希腊文、希伯来文、西里尔文等)域名。每个人都必须更改使用的电子邮件正则表达式,因为这些字符肯定不会被[a-z]/i或\w覆盖。他们都会失败。

毕竟,验证电子邮件地址的最佳方法仍然是向有问题的地址发送电子邮件以验证地址。如果电子邮件地址是用户身份验证(注册/登录等)的一部分,那么您可以将其与用户激活系统完美结合。即,发送带有指向指定电子邮件地址的唯一激活密钥的链接的电子邮件,并且仅当用户使用电子邮件中的链接激活了新创建的帐户时才允许登录。

如果正则表达式的目的只是在UI中快速通知用户指定的电子邮件地址格式不正确,那么最好还是检查它是否与以下正则表达式基本匹配:

^([^.@]+)(\.[^.@]+)*@([^.@]+\.)+([^.@]+)$

就这么简单。你到底为什么会在意名字和域名中使用的字符?输入有效的电子邮件地址是客户的责任,而不是服务器的。即使客户端输入语法上有效的电子邮件地址,如aa@bb.cc,这不能保证它是合法的电子邮件地址。没有一个正则表达式可以涵盖这一点。

电子邮件正则表达式(RFC 5322)

(?im)^(?=.{1,64}@)(?:("[^"\\]*(?:\\.[^"\\]*)*"@)|((?:[0-9a-z](?:\.(?!\.)|[-!#\$%&'\*\+/=\?\^`\{\}\|~\w])*)?[0-9a-z]@))(?=.{1,255}$)(?:(\[(?:\d{1,3}\.){3}\d{1,3}\])|((?:(?=.{1,63}\.)[0-9a-z][-\w]*[0-9a-z]*\.)+[a-z0-9][\-a-z0-9]{0,22}[a-z0-9])|((?=.{1,63}$)[0-9a-z][-\w]*))$

演示https://regex101.com/r/ObS3QZ/1

# (?im)^(?=.{1,64}@)(?:("[^"\\]*(?:\\.[^"\\]*)*"@)|((?:[0-9a-z](?:\.(?!\.)|[-!#\$%&'\*\+/=\?\^`\{\}\|~\w])*)?[0-9a-z]@))(?=.{1,255}$)(?:(\[(?:\d{1,3}\.){3}\d{1,3}\])|((?:(?=.{1,63}\.)[0-9a-z][-\w]*[0-9a-z]*\.)+[a-z0-9][\-a-z0-9]{0,22}[a-z0-9])|((?=.{1,63}$)[0-9a-z][-\w]*))$

# Note - remove all comments '(comments)' before running this regex
# Find  \([^)]*\)  replace with nothing

(?im)                                     # Case insensitive
^                                         # BOS

                                          # Local part
(?= .{1,64} @ )                           # 64 max chars
(?:
     (                                         # (1 start), Quoted
          " [^"\\]*
          (?: \\ . [^"\\]* )*
          "
          @
     )                                         # (1 end)
  |                                          # or,
     (                                         # (2 start), Non-quoted
          (?:
               [0-9a-z]
               (?:
                    \.
                    (?! \. )
                 |                                          # or,
                    [-!#\$%&'\*\+/=\?\^`\{\}\|~\w]
               )*
          )?
          [0-9a-z]
          @
     )                                         # (2 end)
)
                                          # Domain part
(?= .{1,255} $ )                          # 255 max chars
(?:
     (                                         # (3 start), IP
          \[
          (?: \d{1,3} \. ){3}
          \d{1,3} \]
     )                                         # (3 end)
  |                                          # or,
     (                                         # (4 start), Others
          (?:                                       # Labels (63 max chars each)
               (?= .{1,63} \. )
               [0-9a-z] [-\w]* [0-9a-z]*
               \.
          )+
          [a-z0-9] [\-a-z0-9]{0,22} [a-z0-9]
     )                                         # (4 end)
  |                                          # or,
     (                                         # (5 start), Localdomain
          (?= .{1,63} $ )
          [0-9a-z] [-\w]*
     )                                         # (5 end)
)
$                                         # EOS