使用正则表达式进行Ruby Email验证

T0n*_*rdi 46 ruby regex email validation

我有一大堆我正在浏览的电子邮件.很多电子邮件都有拼写错误.我正在尝试构建一个检查有效电子邮件的字符串.

这就是我对正则表达式的看法.

def is_a_valid_email?(email)
  (email =~ /^(([A-Za-z0-9]*\.+*_+)|([A-Za-z0-9]+\-+)|([A-Za-z0-9]+\+)|([A-Za-z0-9]+\+))*[A-Z??a-z0-9]+@{1}((\w+\-+)|(\w+\.))*\w{1,63}\.[a-zA-Z]{2,4}$/i)
end
Run Code Online (Sandbox Code Playgroud)

如果电子邮件为下划线且只有一个句点,则会通过.我有很多电子邮件,名称本身有一个以上的句号.我如何在正则表达式中检查它.

hello.me_1@email.com # <~~ valid
foo.bar#gmail.co.uk # <~~~ not valid
f.o.o.b.a.r@gmail.com # <~~~valid 
f...bar@gmail.com # <~~ not valid 
get_at_m.e@gmail  #<~~ valid
Run Code Online (Sandbox Code Playgroud)

有人可以帮我改写我的正则表达式吗?

Mik*_*H-R 82

TL; DR:

URI::MailTo::EMAIL_REGEXP
Run Code Online (Sandbox Code Playgroud)

原始答案

你好像很复杂,我只想用:

VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
Run Code Online (Sandbox Code Playgroud)

这是从michael hartl的轨道书中取出的

由于这不符合您的点数要求,因此可以简单地进行修改:

VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i
Run Code Online (Sandbox Code Playgroud)

正如CAustin所提到的,还有许多其他解决方案.

编辑:

@installero指出,原始版本对于带有连字符的子域名失败,此版本将起作用(不确定为什么字符类首先缺少数字和连字符).

VALID_EMAIL_REGEX = /\A([\w+\-]\.?)+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i
Run Code Online (Sandbox Code Playgroud)


Jos*_*ter 41

至少从2.2.1开始,它已被内置到标准库中

URI::MailTo::EMAIL_REGEXP
Run Code Online (Sandbox Code Playgroud)

  • `'aa @ aaa'=〜URI :: MailTo :: EMAIL_REGEXP`在这种情况下不起作用。 (5认同)
  • 如果有一个句点,但之后什么都没有,正则表达式将返回 nil。经期后有事或没经期,都会过去。“此要求故意违反 RFC 5322,它为电子邮件地址定义了一种同时太严格(在“@”字符之前)、太模糊(在“@”字符之后)和太松(允许注释、空格字符和以大多数用户不熟悉的方式引用的字符串)在此处具有实际用途。” html.spec.whatwg.org/multipage/input.html#valid-e-mail-address – (2认同)

小智 26

以下是David Celis撰写的一篇精彩文章,解释为什么您可以找到的用于验证电子邮件地址的每个正则表达式都是错误的,包括上面发布的Mike.

来自文章:

本地字符串(@之前的电子邮件地址部分)可以包含以下字符:

    `! $ & * - = ` ^ | ~ # % ' + / ? _ { }` 
Run Code Online (Sandbox Code Playgroud)

但猜猜怎么了?如果你通过用引号括起它来逃避它,你几乎可以使用你想要的任何字符.例如,"查看所有这些空格!"@ example.com是一个有效的电子邮件地址.尼斯.

如果您需要进行基本检查,那么最好的正则表达式就是/@/.

  • 猜测......即使几乎任何东西都可以在电子邮件中提供它的正确引用,实际上99.99%的电子邮件遵循合理的标准格式,并且许多系统会在提交他们认为无效的地址时进行barf(甚至如果是).如果您有这样的组件,那么确保电子邮件地址*合理*以及有效是很重要的 - 特别是如果它是遗留系统的一部分或无法更改/更新的东西. (3认同)
  • 这很公平,但如果您的电子邮件地址中有空格或美元,我不在乎您是否无法使用我的系统。我怀疑当你这样做时你知道自己在做什么。 (2认同)

ilg*_*gam 18

这个更短且更安全:

/\A[^@\s]+@[^@\s]+\z/
Run Code Online (Sandbox Code Playgroud)

常规用于Devise gem.但它对这些值有一些漏洞:

  ".....@a....",
  "david.gilbertson@SOME+THING-ODD!!.com",
  "a.b@example,com",
  "a.b@example,co.de"
Run Code Online (Sandbox Code Playgroud)

我更喜欢使用ruby库中的regexp URI::MailTo::EMAIL_REGEXP

电子邮件验证有一个宝石

电子邮件验证员

  • 谢谢你指点我的'URI :: MailTo :: EMAIL_REGEXP`!感觉像最好的方法,因为这可能比在代码库中的某个地方转储自定义正则表达式更好地维护. (6认同)

kai*_*chn 12

如今Ruby在其标准库中提供了一个电子邮件验证regexp.你可以在URI::MailTo模块中找到它,它是URI::MailTo::EMAIL_REGEXP.在Ruby 2.4.1中,它评估为

/\A[a-zA-Z0-9.!\#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*\z/
Run Code Online (Sandbox Code Playgroud)

但我只是使用常数本身.


san*_*e89 8

接受的答案建议使用URI::MailTo::EMAIL_REGEXP.

但是,此正则表达式将其1234@1234视为有效的电子邮件地址,这在现实应用程序中可能是您​​不希望看到的(例如,如果您尝试将电子邮件发送到类似地址,AWS SES 将引发异常这)。

正如 Darpan 在评论中指出的那样,您只需使用 更改?该正则表达式中的尾随+,它就会按预期工作。生成的正则表达式是:

/\A[a-zA-Z0-9.!\#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+\z/
Run Code Online (Sandbox Code Playgroud)

由于原始的正则URI::MailTo表达式虽然根据规范在技术上是有效的,但恕我直言,对于我们的需求来说毫无用处,因此我们在 Devise 初始化程序中“修复”它。

# in config/initializers/devise.rb, put this at the beginning of the file
URI::MailTo.send(:remove_const, :EMAIL_REGEXP)
URI::MailTo.const_set(:EMAIL_REGEXP, /\A[a-zA-Z0-9.!\#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+\z/)

# And then find `config.email_regexp` (it will already be there in the file) and change it to:
config.email_regexp = URI::MailTo::EMAIL_REGEXP
Run Code Online (Sandbox Code Playgroud)

如果您想知道为什么这个 Monkeypatch 没有放入单独的初始化程序文件中,则必须命名初始化程序文件,以00_xxx.rb使其在设计初始化程序之前加载。这违反了 Rails 文档的建议,该建议实际上建议您在如下情况下使用单个初始化程序:

如果一个初始化器的代码依赖于另一个初始化器中的代码,您可以将它们组合成一个初始化器。这使得依赖关系更加明确,并且可以帮助在应用程序中呈现新概念。Rails 还支持初始化程序文件名的编号,但这可能会导致文件名混乱。

  • 这是最好的答案。我通过直接将 Devise email_regexp 设置为更新的正则表达式来进一步简化了事情: `config.email_regexp = /\A[a-zA-Z0-9.!\#$%&amp;'*+\/=?^_`{| }~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[ a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+\z/` (2认同)

nor*_*raj 6

长话短说

\n

您在互联网上找到的任何自定义正则表达式(包括URI::MailTo::EMAIL_REGEXP, )都是错误的。

\n

这里你应该使用什么:

\n
# The closest thing to RFC_5322\nRFC_5322 = /\\A(?:[a-z0-9!#$%&\'*+\\/=?^_\xe2\x80\x98{|}~-]+(?:\\.[a-z0-9!#$%&\'*+\\/=?^_\xe2\x80\x98{|}~-]+)*|"(?:[\\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])+)\\])\\z/i\n\n# Lighter more practical version RFC_5322 that will be more useful in real life\nRFC_5322_light = /\\A[a-z0-9!#$%&\'*+\\/=?^_\xe2\x80\x98{|}~-]+(?:\\.[a-z0-9!#$%&\'*+\\/=?^_\xe2\x80\x98{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\z/i\n\n# Same as the light version but with length limit enforcing\nRFC_5322_with_length = /\\A(?=[a-z0-9@.!#$%&\'*+\\/=?^_\xe2\x80\x98{|}~-]{6,254}\\z)(?=[a-z0-9.!#$%&\'*+\\/=?^_\xe2\x80\x98{|}~-]{1,64}@)[a-z0-9!#$%&\'*+\\/=?^_\xe2\x80\x98{|}~-]+(?:\\.[a-z0-9!#$%&\'*+\\/=?^_\xe2\x80\x98{|}~-]+)*@(?:(?=[a-z0-9-]{1,63}\\.)[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+(?=[a-z0-9-]{1,63}\\z)[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\z/i\n
Run Code Online (Sandbox Code Playgroud)\n

细节

\n

最后一个定义电子邮件地址格式的 RFC 是RFC5322 - Internet 消息格式

\n

您可以查看3.4.1节。地址规格规范。如果我们只看第一部分,则将@本地部分(左侧)和域部分(右侧)分开。

\n
\n

addr-spec = 本地部分“@”域

\n

本地部分 = 点原子 / 带引号的字符串 / obs 本地部分

\n
\n

例如,本地部分可以包含此处定义的点原子带引号的字符串:

\n\n

它有点复杂,但您的电子邮件地址可以包含许多 ASCII 特殊字符,这些字符被排除在许多正则表达式之外(例如#$&等)。

\n

另一方面,是用以下正则表达式URI::MailTo::EMAIL_REGEXP定义的:ruby/lib/uri/mailto.rb

\n
EMAIL_REGEXP = /\\A[a-zA-Z0-9.!\\#$%&\'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*\\z/\n
Run Code Online (Sandbox Code Playgroud)\n

此正则表达式上方的评论表明他们遵循https://html.spec.whatwg.org/multipage/input.html#valid-e-mail-address中的建议。

\n
\n

有效的电子邮件地址是与以下 ABNF 的电子邮件生成相匹配的字符串,其字符集是 Unicode。该 ABNF 实现了 RFC 1123 中描述的扩展。 [ABNF] [RFC5322] [RFC1034] [RFC1123]

\n
\n

但是WHATWG规范添加了以下注释,这非常重要:

\n
\n

此要求故意违反 RFC 5322,RFC 5322 定义的电子邮件地址语法同时过于严格(在“@”字符之前)、过于模糊(在“@”字符之后)和过于宽松(允许注释、空格)字符,以及以大多数用户不熟悉的方式引用的字符串)在这里有实际用途。

\n
\n

因此 WHATWG 告诉我们他们不尊重标准化电子邮件地址格式的 RFC。他们说 RFC 5322 中的域部分太模糊,但 RFC 5322 给出了这个注释来告诉我们必须检查其他 RFC 以获得更完整的域格式规范:

\n
\n

注意:此处给出了 addr-spec 域部分的自由语法。然而,域部分包含由其他协议指定并在其中使用的寻址信息(例如,[RFC1034]、[RFC1035]、[RFC1123]、[RFC5321])。因此,实现有责任遵守使用地址的上下文的语法。

\n
\n

WHATWG 还告诉我们 RFC 5322 中的本地部分过于严格。但请看一下URI::MailTo::EMAIL_REGEXP遵循 WHATWG 规范的内容:

\n
URI::MailTo::EMAIL_REGEXP.match?(\'.@toto.fr\') # => true\nURI::MailTo::EMAIL_REGEXP.match?(\'-@z\') # => true\nURI::MailTo::EMAIL_REGEXP.match?(\'++++++++.........@z\') # => true\n
Run Code Online (Sandbox Code Playgroud)\n

相反,WHATWG 规范(等等URI::MailTo::EMAIL_REGEXP)太宽松了。

\n

所以我在https://emailregex.com/找到了一个通用电子邮件正则表达式(RFC 5322 官方标准)(参见摘要)。

\n

解释和替代方案可以在https://www.regular-expressions.info/email.html找到。

\n
# Blind RFC 5322\n\\A(?:[a-z0-9!#$%&\'*+/=?^_\xe2\x80\x98{|}~-]+(?:\\.[a-z0-9!#$%&\'*+/=?^_\xe2\x80\x98{|}~-]+)*|"(?:[\\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])+)\\])\\z\n\n# RFC 5322, practical version (omit IP addresses, domain-specific addresses, the syntax using double quotes and square brackets)\n\\A[a-z0-9!#$%&\'*+/=?^_\xe2\x80\x98{|}~-]+(?:\\.[a-z0-9!#$%&\'*+/=?^_\xe2\x80\x98{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\z\n\n# RFC 5322, practical version (similar as previous + length limits enfocing)\n\\A(?=[a-z0-9@.!#$%&\'*+/=?^_\xe2\x80\x98{|}~-]{6,254}\\z)(?=[a-z0-9.!#$%&\'*+/=?^_\xe2\x80\x98{|}~-]{1,64}@)[a-z0-9!#$%&\'*+/=?^_\xe2\x80\x98{|}~-]+(?:\\.[a-z0-9!#$%&\'*+/=?^_\xe2\x80\x98{|}~-]+)*@(?:(?=[a-z0-9-]{1,63}\\.)[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+(?=[a-z0-9-]{1,63}\\z)[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\z\n
Run Code Online (Sandbox Code Playgroud)\n

正如您在下面的屏幕截图中看到的那样,WHATWG / 接受的地址均URI::MailTo::EMAIL_REGEXP无效。

\n

无效的电子邮件地址

\n

让我们在本地做同样的事情:

\n
# Blind RFC 5322\n\\A(?:[a-z0-9!#$%&\'*+/=?^_\xe2\x80\x98{|}~-]+(?:\\.[a-z0-9!#$%&\'*+/=?^_\xe2\x80\x98{|}~-]+)*|"(?:[\\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])+)\\])\\z\n\n# RFC 5322, practical version (omit IP addresses, domain-specific addresses, the syntax using double quotes and square brackets)\n\\A[a-z0-9!#$%&\'*+/=?^_\xe2\x80\x98{|}~-]+(?:\\.[a-z0-9!#$%&\'*+/=?^_\xe2\x80\x98{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\z\n\n# RFC 5322, practical version (similar as previous + length limits enfocing)\n\\A(?=[a-z0-9@.!#$%&\'*+/=?^_\xe2\x80\x98{|}~-]{6,254}\\z)(?=[a-z0-9.!#$%&\'*+/=?^_\xe2\x80\x98{|}~-]{1,64}@)[a-z0-9!#$%&\'*+/=?^_\xe2\x80\x98{|}~-]+(?:\\.[a-z0-9!#$%&\'*+/=?^_\xe2\x80\x98{|}~-]+)*@(?:(?=[a-z0-9-]{1,63}\\.)[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+(?=[a-z0-9-]{1,63}\\z)[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\z\n
Run Code Online (Sandbox Code Playgroud)\n

现在我们可以比较两者(在 Ruby 3.2.0 上):

\n
RFC_5322 = /\\A(?:[a-z0-9!#$%&\'*+/=?^_\xe2\x80\x98{|}~-]+(?:\\.[a-z0-9!#$%&\'*+/=?^_\xe2\x80\x98{|}~-]+)*|"(?:[\\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])+)\\])\\z/i\n
Run Code Online (Sandbox Code Playgroud)\n

警告此测试并不完整,并且未涵盖所有情况。

\n


ins*_*ero 5

我想书中的示例可以改进以匹配-子域中的电子邮件。

VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
Run Code Online (Sandbox Code Playgroud)

例如:

> 'some@email.with-subdomain.com' =~ VALID_EMAIL_REGEX
=> 0
Run Code Online (Sandbox Code Playgroud)