如何在JavaScript中验证电子邮件地址?

5476

在将用户输入发送到服务器或尝试向其发送电子邮件之前,为了防止最基本的打字错误,我想在JavaScript中检查用户输入是否为电子邮件地址。 我该如何实现此目标?


23
@Alex 我添加这个评论的原因是,被接受的答案中建议的正则表达式会禁止现有的有效电子邮件地址,这对客户来说是一个很糟糕的开端。而真正大的问题是,即使地址被接受了,也不能确定它是否有效。可靠地验证提供的电子邮件地址是否有效的唯一方法是发送带有验证链接的邮件。因此,如果您的用例不要求验证电子邮件,请只进行最小限度的@测试;否则,请使用验证邮件。正则表达式只会提供糟糕的用户体验。 - David Mårtensson
7
@mikael1000,好的,但是当你根本不知道是否存在有效的电子邮件时,正则表达式验证有什么用处呢?如果您不想使用验证链接干扰客户,则只需进行最简单的验证<something> at <something>,然后就可以了。这将确保客户至少添加了可能是电子邮件的内容,除此之外的任何操作都是代码浪费,直到您实际进行验证为止。您还可以通过DNS查找来检查域名是否存在。 - David Mårtensson
2
非常相似:*如何使用正则表达式验证电子邮件地址?* - Peter Mortensen
我建议阅读这篇关于电子邮件语法如何工作的文章:https://debounce.io/blog/articles/email-syntax-error-explained/ - Iman
3
我必须承认,我不理解为什么要进行这么多验证,因为即使用户没有使用有效字符,也无法预测其是否进行了基本类型的输入。 只需在开头检查@和至少一个点即可。 - Watts Epherson
显示剩余5条评论
79个回答

6532

在JavaScript中,使用 正则表达式 可能是验证电子邮件地址的最佳方法。查看来自JSFiddle的一系列测试 来自Chromium

const validateEmail = (email) => {
  return String(email)
    .toLowerCase()
    .match(
      /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|.(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
    );
};

以下是一个接受Unicode的正则表达式示例。
const re =
  /^(([^<>()[\]\.,;:\s@\"]+(\.[^<>()[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i;

请记住,我们不能仅依赖JavaScript验证,因为客户端可以轻易地禁用JavaScript。此外,在服务器端进行验证也是非常重要的。
以下代码片段是一个在客户端验证电子邮件地址的JavaScript示例。

const validateEmail = (email) => {
  return email.match(
    /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
  );
};

const validate = () => {
  const $result = $('#result');
  const email = $('#email').val();
  $result.text('');

  if(validateEmail(email)){
    $result.text(email + ' is valid.');
    $result.css('color', 'green');
  } else{
    $result.text(email + ' is invalid.');
    $result.css('color', 'red');
  }
  return false;
}

$('#email').on('input', validate);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<label for="email">Enter email address</label>
<input id="email" type="email">

<p id="result"></p>


716
这个正则表达式会排除掉有效且正在使用的电子邮件地址。请勿使用。可以搜索“RFC822”或“RFC2822”以获取正确的正则表达式。 - Randal Schwartz
73
这甚至不能接受RFC 822中提供的例子。一些简单的情况它无法匹配a@b@c.com、a(b)@c.com等地址。请参阅RFC了解更多信息。这里有一个正则表达式,它不会拒绝任何有效地址[^@]+@[^@]+.[^@]+,并保护免受常见错误的影响。 - Vroo
232
你不能验证电子邮件地址,这是确定的。唯一能够验证电子邮件地址的人是电子邮件地址的提供者。例如,这个答案说这些电子邮件地址:%2@gmail.com, "%2"@gmail.com, "a..b"@gmail.com, "a_b"@gmail.com, _@gmail.com, 1@gmail.com , 1_example@something.gmail.com都是有效的,但 Gmail 永远不会允许任何这些电子邮件地址。你应该通过接受电子邮件地址并向该电子邮件地址发送一封电子邮件消息来实现这一点,其中包含一个用户必须访问以确认有效性的代码/链接。 - Kevin Fegan
19
@KevinFegan 让我们现实一点吧:你不会使用 JavaScript 来确认电子邮件的真实性。当用户注册时,我认为这种验证是完全合理的。您可能不希望发送验证电子邮件到根本不存在的地址。有些人也可能有出站电子邮件限制,因此向 email@localhost我没有电子邮件 或任何其他有趣的用户输入发送电子邮件可能没有意义。 - undefined
1
这是无效的 sean.o'leary@cobbcounty.org,你的代码无法验证它,它说是有效的电子邮件。 - Code Cooker
显示剩余7条评论

1322

我稍微修改了Jaymon的回答,以便提供真正简单的表单验证:

anystring@anystring.anystring

正则表达式:

/^\S+@\S+\.\S+$/

为了防止多个@符号的匹配:

/^[^\s@]+@[^\s@]+\.[^\s@]+$/

以上的正则表达式匹配整个字符串,如果你希望在字符串中任何地方匹配,可以去掉开头的^和结尾的$。下面的例子匹配字符串中的任何位置

如果你确实想要匹配整个字符串,可能需要先使用trim()函数处理字符串。

JavaScript示例函数:

function validateEmail(email) {
  var re = /\S+@\S+\.\S+/;
  return re.test(email);
}
    
console.log(validateEmail('my email is anystring@anystring.any')); // true
    
console.log(validateEmail('my email is anystring@anystring .any')); // false


121
你可以实现一个长度增加20倍的功能,这可能会给少数用户带来问题,并且未来可能无效;或者你可以使用ImmortalFirefly的版本,以确保他们至少付出了努力,使其看起来真实。根据你的应用程序,更有可能遇到某些人因为你不接受他们非传统的电子邮件而生气,而不是由于输入不存在的电子邮件地址而引起问题(无论如何,他们可以通过输入100%有效的RFC2822电子邮件地址,但使用未注册的用户名或域来做到这一点)。已点赞! - user83358
132
@ImmortalFirefly,你提供的正则表达式实际上会匹配 name@again@example.com。请尝试将你的代码粘贴到 JavaScript 控制台中。我相信你的意图是仅匹配整个文本,这需要使用文本开头 '^' 和文本结尾 '$' 的操作符。我正在使用的是 /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test('name@again@example.com') - OregonTrail
8
电子邮件中可以包含多个@符号(作为注释),同时电子邮件并不一定需要包含一个句号。 - ruohola
4
@JoseG。是的。例如,“http://ai”是某人有效的域名,因此他们可以将“a@ai”作为他们的电子邮件地址。 - ruohola
1
@Timo 中的 [^\s@] 表示“既不是空格字符也不是 @ 字符”。[xyz] 表示 x、y 或 z 中的任意一个字符,而 [^xyz] 表示除了 x、y 或 z 之外的任何一个字符。\s 表示“任何空格字符”,\S 表示“任何非空格字符”。 - Jelaby
显示剩余3条评论

902

为了完整起见,这里有另一个符合RFC 2822标准的正则表达式

官方标准被称为RFC 2822。它描述了有效电子邮件地址必须遵循的语法。您可以(但不应该继续阅读)使用此正则表达式来实现它:

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

(...) 如果我们省略使用双引号和方括号的语法,那么我们可以得到一个更实用的RFC 2822实现。 它仍然可以匹配今天实际使用的99.99%的所有电子邮件地址。

[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?

您可以进一步更改的是允许任何两个字母的国家代码顶级域,以及只有特定的通用顶级域。 此正则表达式可以过滤虚假的电子邮件地址,例如asdf@adsf.adsf。 随着新增顶级域名的不断增加,您需要更新它

[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+(?:[A-Z]{2}|com|org|net|gov|mil|biz|info|mobi|name|aero|jobs|museum)\b

因此,即使遵循官方标准,仍然需要进行权衡。 不要盲目地从在线库或讨论论坛复制正则表达式。始终使用您自己的数据和应用程序测试它们。


111
请注意,“在今天的实际使用中”可能在编写该代码时是有效的,那是在200x年。该代码很可能会在那一年之后继续使用。(如果我因为每次修复“嗯,除了那些特定的顶级域名之外,没人会使用4个或更多字母的顶级域名”而得到10美分的话,我就可以垄断世界的铜镍市场;) ) - Piskvor left the building
5
请注意,这并不能捕获一些有效的电子邮件地址,比如这些表情符号的地址:https://mailoji.com/。 - Toastrackenigma
8
如果有人在电子邮件中使用表情符号,那么他们不值得订阅我的网站。就这么简单。 - Christian Vincenzo Traina
@ChristianVincenzoTraina,最后一个拒绝我的.rocks域名的电子邮件地址。如果这阻止我订阅你的通讯,那么也许对我来说不接收Ignorant Daily到我的收件箱是好事,但是阻止我使用此域名的服务列表包括:全国范围的公共交通系统、一家航空公司、医疗服务... - lnl
1
@lnl 最后一个代码片段是无法维护的。自2009年以来,顶级域名数量激增。 - Christian Vincenzo Traina

524

哇,这里有很多复杂性。如果你只想捕获最明显的语法错误,我会这样做:

^\S+@\S+$

通常,它会捕捉用户最明显的错误并确保表单大部分正确,这正是JavaScript验证的目的。

编辑:我们还可以使用 '.' 来检查电子邮件地址。

/^\S+@\S+\.\S+$/

117
发送电子邮件并查看结果是验证电子邮件地址的唯一可靠方法,只需要进行简单的正则表达式匹配即可,不需要做更多的事情。 - kommradHomer
4
由于它包含空格字符,因此它不会接受"mohit Atray"@gmail.com。也许我们应该只使用/^\S.*@\S+$/正则表达式。 - Mohit Atray
1
仍然无效。只要本地部分被正确转义(用双引号),空格是允许的。 - Randal Schwartz
@RandalSchwartz,我认为Mohit已经写了关于@前面空格的评论了。 - Timo
如何添加 {2,6} 以确保句点后的字符串长度在 2 到 6 个字符之间? - Avatar

375

一旦你决定使用正则表达式来验证电子邮件,就必须要明白一件事情:这可能不是一个好主意。一旦你认识到这一点,就有很多实现可以让你完成其中的一半,这篇文章概括得很好。

然而简单来说,唯一可以绝对肯定用户输入的内容是电子邮件的方法就是实际发送一封电子邮件并观察结果。除此之外都只是猜测。


77
“regex invalid”的电子邮件地址几乎总是有效的,因为无论您使用什么正则表达式来验证电子邮件地址,它几乎肯定都是错误的,并且将排除有效的电子邮件地址。 电子邮件地址是“name_part@domain_part”,在name_part中几乎任何内容,包括“@”,都是有效的;地址“foo@bar@machine.subdomain.example.museum”是合法的,尽管必须转义为“foo@bar@machine....”。一旦电子邮件到达域名(例如'example.com'),该域名可以将邮件路由“本地”,因此“奇怪”的用户名和主机名可以存在。 - Stephen P

323

HTML5本身具有电子邮件验证功能。如果您的浏览器支持HTML5,则可以使用以下代码。

<form>
  <label>Email Address
    <input type="email" placeholder="me@example.com" required>
  </label>
  <input type="submit">
</form>

jsFiddle链接。

来自HTML5规范

有效的电子邮件地址是一个与以下ABNF中的email产生式相匹配的字符串,其字符集为Unicode。

email   = 1*( atext / "." ) "@" label *( "." label )
label   = let-dig [ [ ldh-str ] let-dig ]  ; limited to a length of 63 characters by RFC 1034 section 3.5
atext   = < as defined in RFC 5322 section 3.2.3 >
let-dig = < as defined in RFC 1034 section 3.5 >
ldh-str = < as defined in RFC 1034 section 3.5 >

这一要求是RFC 5322的故意违规行为,该标准定义了一种电子邮件地址语法,它在“@”字符之前过于严格,在“@”字符之后过于模糊,并且允许评论、空格字符和带引号的字符串以大多数用户不熟悉的方式使用,因此对于本场景来说没有太多实际用途。

以下JavaScript和Perl兼容的正则表达式是上述定义的实现。

/^[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])?)*$/

54
这很好,但问题在于它必须位于一个form标签内并由一个submit输入提交,而不是每个人都能做到。另外,你不能真正地设计错误信息的样式。 - Jason
起初我觉得这个答案很有用,但是我试了一些无效的电子邮件地址,正则表达式却说它们是有效的...例如:bar@domain(缺少扩展名),A@b@c@example.com(多个@)等等(参见https://en.wikipedia.org/wiki/Email_address#Examples)。我在这里找到了一个好的正则表达式:https://emailregex.com。 - Thomas Champion
2
@ThomasChampion 多个 "@" 是有效的。不过第一个点是有效的。 - KNP
2
@KNP 多个 @ 仅在额外的 "@" 出现在用户名部分并被双引号括起来时才有效。似乎自2013年以来第二个点(缺少扩展名)也不再有效(https://www.icann.org/en/announcements/details/new-gtld-dotless-domain-names-prohibited-30-8-2013-en),但是这取决于情况,我猜有人可能认为扩展名是可选的。最后,我选择了 joi 中的 address 模块:https://joi.dev/module/address/api/?v=4.1.0#emailisvalidemail-options 来验证我的应用程序中的电子邮件地址。 - Thomas Champion

208

我发现这是最好的解决方案:

/^[^\s@]+@[^\s@]+\.[^\s@]+$/

它允许以下格式:

1. prettyandsimple@example.com
2. very.common@example.com
3. disposable.style.email.with+symbol@example.com
4. other.email-with-dash@example.com
5. #!$%&'*+-/=?^_`{}|~@example.org
6. "()[]:,;@\\\"!#$%&'*+-/=?^_`{}| ~.a"@example.org
7. " "@example.org(引号间有空格)
8. üñîçøðé@example.com(本地部分使用 Unicode 字符)
9. üñîçøðé@üñîçøðé.com(域名部分使用 Unicode 字符)
10. Pelé@example.com(拉丁文)
11. δοκιμή@παράδειγμα.δοκιμή(希腊文)
12. 我買@屋企.香港(中文)
13. 甲斐@黒川.日本(日文)
14. чебурашка@ящик-с-апельсинами.рф(西里尔文)

显然,它具有很强的适应性,并允许重要的国际字符,同时仍强制遵循基本的 anything@anything.anything 格式。它会阻止 RFC 技术上允许的空格,但它们非常罕见,我很高兴这样做。


167

在现代浏览器中,你可以使用纯JavaScript和DOM基于@Sushil的回答进行构建:

function validateEmail(value) {
  var input = document.createElement('input');

  input.type = 'email';
  input.required = true;
  input.value = value;

  return typeof input.checkValidity === 'function' ? input.checkValidity() : /\S+@\S+\.\S+/.test(value);
}

我已经在这个fiddle中提供了一个示例http://jsfiddle.net/boldewyn/2b6d5/。结合特征检测和来自Squirtle's Answer的基本验证,它可以避免使用正则表达式,并且不会在旧浏览器上出现问题。


8
这个回答应该是最好的答案。让浏览器厂商拥有正则表达式专家维护复杂的电子邮件正则表达式。普通的前端开发人员在构建收集电子邮件的表单时往往没有时间掌握冗长的正则表达式。是的,你必须依赖供应商提供的正则表达式,但如果您需要更复杂的内容,请在服务器上执行此操作或发送实际电子邮件并检查响应。 - Matt
这是几乎完美的解决方案。唯一需要添加的是检查空字段的功能。HTML5的“email”类型接受空字符串作为有效输入。 - NurShomik
4
我可能会重复一遍,但是你注意到 input.required = true; 这行代码了吗? - Boldewyn
1
值得考虑的是:它对未来的更改具有强大的鲁棒性,因为它不会硬编码实际的正则表达式。我喜欢这个方面。 - oelna
2
就像@KarlStephen说的那样。想象一下一个用于内部网络应用的Web应用程序。浏览器拒绝诸如user@localhost这样的电子邮件地址会比没有任何作用更糟糕。它将会主动阻碍正确填写表单的过程。 - Boldewyn
显示剩余8条评论

96

JavaScript可以匹配正则表达式:

emailAddress.match( / some_regex /);

这是一个用于电子邮件的RFC22正则表达式:

^((?>[a-zA-Z\d!#$%&'*+\-/=?^_`{|}~]+\x20*|"((?=[\x01-\x7f])[^"\\]|\\[\x01-\x7f])*
"\x20*)*(?<angle><))?((?!\.)(?>\.?[a-zA-Z\d!#$%&'*+\-/=?^_`{|}~]+)+|"((?=[\x01-\x
7f])[^"\\]|\\[\x01-\x7f])*")@(((?!-)[a-zA-Z\d\-]+(?<!-)\.)+[a-zA-Z]{2,}|\[(((?(?<
!\[)\.)(25[0-5]|2[0-4]\d|[01]?\d?\d)){4}|[a-zA-Z\d\-]*[a-zA-Z\d]:((?=[\x01-\x7f])
[^\\\[\]]|\\[\x01-\x7f])+)\])(?(angle)>)$

3
@Kato:它使用了一些不兼容的扩展,包括(?>来停止回溯和(?<angle><)…(?(angle)>)来避免提供冗长的| - Ry-
match 方法返回一个数组,而对于这种情况,返回布尔值的 test 方法会更好。 - iPzard

90
所有的电子邮件地址都包含'at'符号(即@)。测试必要条件:
email.includes('@')

或者,如果您需要支持IE/旧版浏览器:

email.indexOf('@') > 0

不要找更复杂的方法。即使您可以完美地确定电子邮件是否符合RFC句法规则,也无法告诉您它是否属于提供它的人。这才是真正重要的。
要测试这一点,请发送验证消息。

22
如果有多个“@”符号怎么办?其他受限制的符号呢?这种验证不可信... - iwazovsky
a@b 是一个有效的电子邮件吗? - Aravin
4
比大多数都要好,是的,您可以在这个电子邮件地址中使用多个 "@" 符号,但那也可能是一个有效的电子邮件地址,例如“@”@mydomain.jskd或elldffs(这是 @ 的注释)@mydomain.kjfdij。两者在语法上都是有效的电子邮件地址。 - David Mårtensson
2
@Aravin 是的,没错。 - ruohola
2
如果您正在使用字符串级别的验证来确定是否可以“信任”输入的电子邮件,则已经出错了。除了捕获错误之外,将其用于其他任何目的都是徒劳无功的,而大多数未被此类验证捕获的错误可能仍然看起来像有效的电子邮件,因此没有正则表达式可以捕获它们。 - John Montgomery

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