如何在Javascript和PHP中验证非英语(UTF-8编码)的电子邮件地址?

12

我目前正在开发的网站的一部分包含注册流程,用户需要提供他们的电子邮件地址。最近我意识到非ASCII基础域名是可能的(电子邮件也是如此)。

我的后端是utf-8编码的MySQL,我希望任何用户(具有不同语言环境)都能够输入他们的电子邮件,但我不知道如何验证这种类型的电子邮件地址。

目前我正在测试jquery工具,它可以正确验证英文电子邮件地址,但无法验证非ASCII电子邮件地址。同时我需要在php中进行服务器端的验证。是否有一个正则表达式可以验证这种类型的电子邮件地址?

我已经尝试过下面这个正则表达式,但它在jquery工具中无法通过验证(这只是演示用的例子,我自己也不理解这个正则表达式):

闪闪发光@闪闪发光.com

当他们使用自己的IME输入英文电子邮件地址(jonesmith@somemail.com)时会发生什么?当前我们用于验证英语邮件的正则表达式是否可以验证这种情况。目前我不必担心该电子邮件是否存在。

谢谢


1
你可以使用\p{L}来匹配任何Unicode字母 - mario
1
@mario,使用您的示例,刚刚发布了一个答案! - Ilia
7个回答

15

尝试验证电子邮件地址可能并不是一个好主意。由于规范(RFC5321, RFC5322)允许很大的灵活性,使用正则表达式进行验证实际上是不可能的,而使用函数进行验证也需要花费大量的工作。这导致大多数电子邮件验证方案最终会拒绝许多有效的电子邮件地址,给用户带来很大不便。(远远最常见的例子是不允许使用“+”字符。)

与其验证电子邮件地址是否有效,更有可能出现的情况是用户(无意或故意地)输入错误的电子邮件地址,因此实际上进行验证只会为我们带来很小的收益,同时还可能带来成本风险。

我建议在客户端上仅检查是否存在“@”字符,然后发送确认电子邮件以进行验证;这是最实用的验证方式,同时可以确认该地址的正确性。


谢谢您的建议。我想知道像sendmail或phpmail这样的邮件发送程序是否可以直接处理这个UTF-8编码的电子邮件地址,而无需对我进行任何修改。 - Deepak Shrestha
6
尽管使用正则表达式验证电子邮件技术上是几乎不可能的,但我对这个答案作为一种常规解决方案持不同意见。在大多数现实世界(非理论)应用中,您将在数据库中存储相关电子邮件地址,并/或者将来对其进行一些操作。允许任何旧的UTF-8字符串无阻碍地传递到数据层是一个糟糕的主意。与其有100%的概率遭受巧妙注入攻击,我宁愿拒绝一些“离谱”的有效电子邮件地址。在现实世界中,“hi”\ ~e^ery!@myhost不会经常出现。 - s.co.tt

2

根据Mario提供的方法,经过一些尝试,我得出了以下用于验证非标准电子邮件地址的正则表达式:

^([\p{L}\_\.\-\d]+)@([\p{L}\-\.\d]+)((\.(\p{L}){2,63})+)$

此代码可以验证任何使用各种Unicode字母编写的有效电子邮件地址,TLD长度限制为2到63个字符。

请检查它并让我知道是否有任何缺陷。

在线示例


这适用于PHP,不适用于JavaScript。 - D.A.H
1
@D.A.H JavaScript不支持Unicode快捷方式。您可以使用Steven Levithan的XRexExp包与Unicode附加组件-http://xregexp.com/plugins/。 - Ilia
多好的电子邮件地址! :-) 好的,我已经更新了正则表达式。下划线确实被许多电子邮件提供商允许使用。谢谢。 - Ilia
@IliaRostovtsev 抱歉,直到现在才看到您的评论。已点赞。谢谢! - Jeremy
2021年注意事项:PCRE中的UTF-8增强功能(在PHP 7.3中的preg_replace中进行了测试)可能更喜欢使用\p{Pd}代替-表示连字符,以及\p{Nd}代替\d表示十进制数字,如果您的代码在升级后似乎无法正常工作。 - Jeff Clayton

2
自 PHP 5.2 版本起,内置了电子邮件地址验证。但我不确定它是否适用于 UTF-8 编码的字符串:
echo filter_var($email, FILTER_VALIDATE_EMAIL);

原始 PHP 源代码中,您将找到用于验证电子邮件的正则表达式,这可以在使用 PHP < 5.2 时手动验证。 更新 idn_to_ascii() 可以用于“将域名转换为 IDNA ASCII 形式。” 然后可以使用 filter_var($email, FILTER_VALIDATE_EMAIL); 进行验证。
// International domains
if (function_exists('idn_to_ascii') && strpos($email, '@') !== false) {
    $parts = explode('@', $email);
    $email = $parts[0].'@'.idn_to_ascii($parts[1]);
}
$is_valid = filter_var($email, FILTER_VALIDATE_EMAIL);

谢谢。我猜这是服务器端验证朝正确方向迈出的一步。 - Deepak Shrestha
9
不,它不支持UTF-8! - Ilia

0

正则表达式可能是这样的:

[^ ]+@[^ ]+\.[^ ]{2,6}

4
TLD并不限制2-6个字符,而且鉴于ICANN允许创建任意TLD的决定,很可能在不久的将来会出现诸如".microsoft"这样的地址。此外,如果空格被正确转义,它们可以包含在有效的电子邮件地址中。 - Jeremy
2
没问题,将 {2,6} 扩展到任何你想要的长度。它也可以被替换为 [^]。 - powtac
谢谢提供信息。这种验证对我来说似乎是一项艰巨的任务。 - Deepak Shrestha
这不是一个简单的问题。尽可能用你的正则表达式覆盖更多内容。查看此链接以了解在PERL中实际正则表达式的样子:http://ex-parrot.com/~pdw/Mail-RFC822-Address.html - powtac

0

这个想法来自于JavaScript教程页面。它很基础,但对我来说很有效,而且不用担心正则表达式和Unicode标准的复杂性。

客户端验证

if(!$.trim(value).length) {
    return false;
}
else {

    AtPos = value.indexOf("@");
    StopPos = value.lastIndexOf(".");

    if (AtPos == -1 || StopPos == -1) {
        return false;
    }

    if (StopPos < AtPos) {
        return false;
    }

    if (StopPos - AtPos == 1) {
        return false;
    }

    return true;
}

服务器端验证

if(!isset($_POST['emailaddr']) || trim($_POST['emailaddr']) == "") {
    //Error: Email required
}
else {
    $atpos = strpos($_POST['emailaddr'],'@');
    $stoppos = strpos($_POST['emailaddr'],'.');

    if(($atpos === false) || ($stoppos === false)) {
        //Error: invalid email
    }
    else {
        if($stoppos < $atpos) {
            //Error: invalid email
        }
        else {
            if (($stoppos-$atpos) == 1) {
            //Error: invalid email
        }
    }
}

虽然还存在一些漏洞,但我想用户不会拿这个东西来嬉闹。对于严肃的事情,按照"Jeremy Banks"所建议,需要进行真正的验证。

希望这对其他人也有所帮助。

感谢大家的支持与关注。


-1

关于这个话题,我非常喜欢这个页面,以至于我建立了一个揭示验证错误网站的博客(感谢贡献 - 不要让你的网站成为其中之一!)。

就正则表达式的使用而言,“它是错误的”这种说法往往缺少替代方案,而且严格遵循 RFC 的验证并不是那么重要 - 例如,虽然noddy+!#$%&'*-/=?+_{}|~test@gmail.com是一个完全有效的地址,但由于惊人的大量用户甚至无法正确输入“hotmail”,拒绝该地址也不太过分。某些域名对用户名也有相当大的限制,尤其是 hotmail。因此,我支持可以证明是合理的正则表达式,并且我的最爱来源是这个页面,虽然我不喜欢他们目前的 JS “赢家”,如果他们能建立一个公共测试页面就更好了。

jQuery的验证插件使用这个正则表达式,它有趣地构建,风格相当类似(但更小!)于@powtac链接的前鹦鹉正则表达式(实际上是我的ISP!)。


-3

这是关于什么的:

mb_internal_encoding("UTF-8");
mb_regex_encoding("UTF-8");
mb_ereg('[\w]+@[\w]+\.com',$mail,'UTF-8');

那个正则表达式并没有真正进行任何验证(会返回错误的结果和漏报的情况)。 - symcbean
\w 不匹配 . 或 - (这两个字符对于域名和电子邮件都是有效的字符)。 - Edson Medina
@EdsonMedina >所有的电子邮件都以.com结尾< 这取决于情况。这个答案更多是一个例子。如果您建立了一个公司内部网页,并且需要验证邮件地址以允许仅限公司内部地址,则可以采用这种方式。当然,需要严格的邮件语法。 - The Bndr

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