0

验证电子邮件不是一种选择:(

我需要使用一组非常具体的规则来验证电子邮件地址。我已经尝试过 Apache Commons 库以及 JavaMail 库;尽管这两个都遵守 RFC 2822,但根据我的规则,一些无效的电子邮件还是可以通过。我一直在尝试使用正则表达式(正则表达式?)无济于事。我知道我知道。正则表达式不是最佳选择,可能会花费大量时间并增加复杂性。尽管如此,我仍然认为,因为我已经用不那么难的术语概述了规则,因此为这个特定实例构建一个就足够了。

规则:

  1. 电子邮件地址的本地部分可以使用以下任何一种:
    • 大写和小写字母
    • 数字 0-9
    • 特殊字符: , !# $ % ^ & * ( ) ' ` + = - _ { } | ~
    • 句点,但不能以句点开头或结尾
    • 可能不包含连续的句点
  2. 电子邮件的本地和域部分之间必须有一个 At 符号 (@)
  3. 域只能包含字母、数字、下划线、句点和连字符
    • 不能以连字符开头
    • 不能以连字符结尾
    • 不能包含两个连续的连字符
  4. 电子邮件的域和 TLD 部分之间必须有一个句点
    • TLD 只能包含字母
    • TLD 不得以句点结尾

到目前为止,我一直在尝试使用以下正则表达式:

^((?!.\.{2,}.)[^.][-a-zA-Z0-9_.\!\@\#\$\%\^\&\*\(\)\, \'\+\=\`\{\|\}\~\-]+[^.])@((?!.\-{2,}.)[^-_][-a-zA- Z0-9_.]+[^-_]\.[a-zA-z]+)$


^((?!.\.{2,}.)[^.][-a-zA-Z0-9_.!@#$%^&*(),'+=`{|}~-]+[^.])@((?!.\-{2,}.)[^-_][-a-zA-Z0-9_.]+[^-_]\.[a-zA-z]+)$

这仍然因无效电子邮件而失败(例如 justlikethat@gm--ail.com)。

正则表达式我错过了什么或做错了什么?有没有另一种方法可以确保电子邮件在没有正则表达式的情况下符合要求?

提前致谢!

PS 这是在 Java 中,所以上述正则表达式中的所有转义字符都必须进行双重转义(例如\.is \\.)。我也一直在使用Regexper来帮助我可视化这一点,因为我显然不是正则表达式专家。

4

2 回答 2

2

I suggest:

Split on the @ symbol. Split on the last period (using String#substring and String#lastIndexOf). Now you have the local part, the domain and the TLD all in separate strings, use if-statements to validate. If there are any rules applicable to all (2 consecutive periods?), do that before splitting. Much simpler to get right, much simpler to understand, much simpler to maintain.

But, if you really want to stick to regex, here's a few things I've seen:

The [^.] before the @ should be (?<!\.), otherwise the last character before the @ can be just about anything.

. is just one character, so (?!.\-{2,}.) and (?!.\.{2,}.) does not do what you think it does. Just making it .* seems to fix it. And you don't need to check any characters after the things you're looking for.

It hasn't been explicitly stated, but I presume the domain and TLD can't contain 2 successive periods either. If this is allowed, the first part of the regex needs to be (?!.*\.{2,}.*@) to stop at the @.

If you use String#matches, the ^ and $ isn't required.

There's some unneeded ()'s.

Final regex:

(?!.*\.{2,})[^.][-a-zA-Z0-9_.!@#$%^&*(),'+=`{|}~-]+(?<!\.)@(?!.*\-{2,})[^-_][-a-zA-Z0-9_.]+[^-_]\.[a-zA-z]+

If you choose to stick to regex, I suggest extensive commenting:

String regex =
          "(?!.*\\.{2,})" // doesn't contain 2 consecutive .'s
       // local part
          + "[^.]" // doesn't start with a .
          + "[-a-zA-Z0-9_.!@#$%^&*(),'+=`{|}~-]+" // valid chars for local part
          + "(?<!\\.)" // last char of local part isn't a .
       // at symbol
          + "@"
       // domain
          ...

It might seem like overkill, but you'll wish you had if you try to maintain it a few months down the line, especially if you haven't touched any regex in those months.

于 2013-05-22T20:44:18.867 回答
1

普遍的看法是电子邮件对于单个正则表达式来说太复杂了。通过查看 SMTP 服务器是否可以发送电子邮件地址更容易检查它。你已经被告知了。

因此,假设您需要预先验证一个地址(并假设它只是电子邮件部分,而不是您可以拥有的所有好东西,例如 unicode 名称等),那么我的建议是:

  1. 将问题分解为更小的部分
  2. 给每个部分一个方法
  3. 验证每个部分(可能在循环中)。
  4. 使用正则表达式和标准逻辑的组合来确保它是有效的(根据你的规则)

这是留下一个有点合理的系统的唯一现实方法,这个系统对于下次看到代码的可怜的傻瓜来说是可维护和理解的。

例如

private void validateNamePart(String npart) {
  if (!npart.matches("")) {
    throw new .....;
  }
}

private void validateName(String name) {
  int parts = 0;
  for (String npart : name.split("\\.")) {
    validateNamePart(npart);
    parts++;
  }
  if (parts == 0) {
     throw ....;
  }
}

private void validateDomainPart(String dpart) {
  ....
}

private void validateDomain(String domain) {
  ....
}

public void validateEMail(String email) {
  String parts = email.split("@");
  if (parts.length == 2) {
    validateName(parts[0]);
    validateDomain(parts[1]);
  } else {
    throw ....
  }
}
于 2013-05-22T20:13:53.263 回答