正则表达式识别电子邮件地址难吗?

62

我最近在某个地方读到,编写匹配电子邮件地址的正则表达式,考虑标准的所有变化和可能性是极其困难的,明显比人们最初想象的要复杂得多。

为什么会这样呢?

是否有已知且经过验证的正则表达式可以完全实现这一点?

有哪些很好的替代方法可以用于匹配电子邮件地址而不使用正则表达式?


我认为你所读的内容不是关于“根据标准验证电子邮件地址”,而是“验证实际电子邮件地址”。即使措辞相同,两者之间的区别并不微小。目前,下面的答案混淆了这两个概念。也许您可以澄清一下问题? - bzlm
我之前写了一篇关于这个的博客文章 -- 在这里:如何使用正则表达式验证电子邮件地址,它指出了捕获所有不同边缘情况的一些挑战。 - Kevin Bedell
1
用一条正则表达式来解析复杂文本是常见的蠢事。但是用一组正则表达式来解析复杂文本(比如 C 语言源代码)却很容易,例如使用 lex 和 yacc。这种方法还支持递归。把责任归咎于 Larry。 :) - Sam Watkins
显示剩余2条评论
19个回答

3

补充一个比@mmaibaum列出的正则表达式更简单的正则表达式:

^[a-zA-Z]([.]?([a-zA-Z0-9_-]+)*)?@([a-zA-Z0-9\-_]+\.)+[a-zA-Z]{2,4}$ 

它并不是绝对可靠的,当然也不能覆盖整个电子邮件规范,但它确实可以很好地满足大多数基本要求。更好的是,它相当易懂,并且可以进行编辑。

这段内容摘自HouseOfFusion.com的讨论,这是一个世界级的ColdFusion资源。


5
那个正则表达式甚至不能匹配username+detail@example.com,更不用说user@example.museum了。如果这就是某些人心目中的世界级ColdFusion资源,那么谢天谢地我不会用CF编程。 - womble
1
正如我的描述所述,它并不是要详尽无遗。它应该是(相对)简单明了的,并且易于修改。 - Ben Doom
1
你真的要根据一些语言使用者多年前为解决在现在已经不存在的问题而提出的解决方案来评判一门语言吗? - Ben Doom
我没有创建正则表达式的经验,但如果您希望对“some.one@somewhere.tld”进行正确验证,请使用以下正则表达式(可通过Expresso进行验证):^a-zA-Z?@([a-zA-Z0-9-_]+.)+[a-zA-Z]{2,4}$ - quantme

2
如果您正在运行.NET Framework,只需尝试实例化MailAddress对象并捕获FormatException(如果它失败),或者提取Address(如果成功)。不要陷入关于捕获异常的性能问题(真的,如果这只是一个单独的Web窗体,它不会产生太大的影响),.NET框架中的MailAddress类经过了相当完整的解析过程(它没有使用RegEx)。打开Reflector并搜索MailAddressMailBnfHelper.ReadMailAddress(),以查看所有它所做的花式操作。比我聪明的人在微软花了很多时间构建该解析器,我将在实际发送电子邮件到该地址时使用它,因此我也可以使用它来验证传入的地址。

2
这很困难,因为根据电子邮件规范RFC 2822,有很多东西可以成为有效的电子邮件地址。你通常看不到的字符比如+也是规范允许的字符。
http://regexlib.com上有一个完整的电子邮件地址部分,这是一个很好的资源。建议您确定对您重要的标准并找到符合条件的内容。大多数人实际上并不需要完全支持规范允许的所有可能性。

大多数人实际上并不需要完全支持规范允许的所有可能性。 - David Schmitt
@David Schmitt:地址Abc@def@example.com,customer/department=shipping@example.com和!def!xyz%abc@example.com都是有效的。 然而,在生产站点中,99.99%的人不会遇到这些类型的地址。 - Wayne

1

这个Java类中有一个验证器: http://www.leshazlewood.com/?p=23

这是由Shiro的创建者(以前是Ki,以前是JSecurity)编写的。

测试电子邮件地址有效性的优缺点:

有两种验证电子邮件的正则表达式:

  1. 太宽松的正则表达式。
  2. 太严格的正则表达式。

由于某些字符串可能看起来像有效的电子邮件地址,但实际上并不会发送到任何人的收件箱,因此正则表达式无法匹配所有有效的电子邮件地址和没有有效的电子邮件地址。唯一测试电子邮件是否真正有效的方法是向该地址发送电子邮件并查看是否收到某种响应。考虑到这一点,过于严格匹配电子邮件的正则表达式似乎没有什么意义。

我认为大多数要求电子邮件正则表达式的人都是在寻找第一种选项,即过于宽松的正则表达式。他们想测试一个字符串并查看它是否像电子邮件,如果它绝对不是电子邮件,则可以告诉用户:“嘿,您应该在这里输入电子邮件,而这绝对不是有效的电子邮件。也许您没有意识到这个字段是用来输入电子邮件的,或者可能有错别字”。

如果用户输入的字符串看起来很像有效的电子邮件,但实际上不是,则应由应用程序的其他部分处理此问题。


1

许多人尝试过,也有很多人接近成功。你可能想阅读wikipedia文章其他一些资源

具体来说,你需要记住许多网站和电子邮件服务器对电子邮件地址的验证进行了放宽,因此它们并没有完全实现标准。不过这已经足够保证电子邮件始终能正常工作了。


1

试试这个:

"(?:[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])+)\])"

请在这里查看详细信息。

然而,与其实现RFC822标准,也许从另一个角度来看会更好。如果邮件服务器不遵循标准,标准的内容就不是那么重要了。因此,我认为最好模仿最流行的邮件服务器在验证电子邮件地址时所做的事情。


我在一个类似的问题上发布了相同的链接: https://dev59.com/THVC5IYBdhLWcg3wsTZi我发现它很好地解释了这种情况! - brasskazoo

0

有人能提供一些关于这个的见解吗?

是的,这是一个非常复杂的标准,允许很多今天没有人真正使用的东西。 :)

是否有任何已知和经过验证的正则表达式可以完全实现这一点?

这里是一个尝试完全解析整个标准的方法...

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

除了使用正则表达式匹配电子邮件地址,还有哪些好的替代方法?

我猜想可以在你正在使用的任何语言中使用现有框架来完成这个任务?但是那些框架内部可能会使用正则表达式。它是一个复杂的字符串。正则表达式被设计用于解析复杂的字符串,所以这确实是您最好的选择。

编辑:我应该补充说,我链接到的正则表达式只是为了好玩而已。 我不赞成使用像那样复杂的正则表达式-有些人说“如果你的正则表达式超过一行,那么它肯定有错误”。 我链接它是为了说明标准有多么复杂。


1
不完全是。Regexps 是一种快速编写分析字符串的简单方法,无论其是否复杂。它们并不适用于处理那些从数学上来说超出了它们能力范围的事情,或者那些需要疯狂而难以维护的正则表达式的事情。 - Marcin
1
有没有任何东西可以处理超出它们的数学问题? :P - Lars Westergren

0
为了让这篇文章更完整,对于 PHP 来说,也有一种内置的语言函数来验证电子邮件。
对于 PHP,使用漂亮的 filter_var 函数和特定的 EMAIL 验证类型 :)
在 PHP 中不再需要疯狂的电子邮件正则表达式 :D
var_dump(filter_var('bob@example.com', FILTER_VALIDATE_EMAIL));

http://www.php.net/filter_var


0
在尝试创建正则表达式来验证电子邮件时,似乎总会有一个无法解释的格式。虽然电子邮件中存在一些无效字符,但基本格式是local-part@domain,本地部分最多大约64个字符,域名最多大约253个字符。除此以外,它就像“野生西部”一样。
我认为答案取决于您对已验证电子邮件地址的定义以及业务过程的容忍度。正则表达式非常适合确保电子邮件格式正确,而且您知道有许多可以工作的变体。以下是几种变化:
变体1:
(?:[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])+)\])

变量2:

\A(?:[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-][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])+)\])\z

仅仅因为一封电子邮件在语法上是正确的,并不意味着它是有效的。

一封电子邮件可以遵循 RFC 5322 并通过正则表达式,但对于电子邮件实际的可投递性没有真正的洞察力。如果您想知道这封电子邮件是否是虚假邮件、一次性邮件或无法投递的邮件,或者是否是已知的机器人呢?如果您想排除粗俗或有问题的电子邮件怎么办?顺便说一下,我在一家数据验证公司工作,我希望说明清楚我工作的单位是Service Objects。但作为电子邮件验证领域的专业人士,我认为我们提供的解决方案比正则表达式提供更好的验证。请随意查看,我认为它会有很大帮助。您可以在我们的开发指南中了解更多信息。它实际上进行了许多酷炫的电子邮件检查和验证。

以下是一个例子:

电子邮件:mickeyMouse@gmail.com

{
  "ValidateEmailInfo":{
      "Score":4,
      "IsDeliverable":"false",
      "EmailAddressIn":"mickeyMouse@gmail.com",
      "EmailAddressOut":"mickeyMouse@gmail.com",
      "EmailCorrected":false,
      "Box":"mickeyMouse",
      "Domain":"gmail.com",
      "TopLevelDomain":".com",
      "TopLevelDomainDescription":"commercial",
      "IsSMTPServerGood":"true",
      "IsCatchAllDomain":"false",
      "IsSMTPMailBoxGood":"false",
      "WarningCodes":"22",
      "WarningDescriptions":"Email is Bad - Subsequent checks halted.",
      "NotesCodes":"16",
      "NotesDescriptions":"TLS"
  }
}

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