【问题标题】:Regex split email address正则表达式拆分电子邮件地址
【发布时间】:2011-10-14 14:52:47
【问题描述】:

我需要一些关于 php 正则表达式的帮助,我想将电子邮件地址“johndoe@example.com”“拆分”为“johndoe”和“@example.com”

到目前为止,我有这个:preg_match('/<?([^<]+?)@/', 'johndoe@example.com', $matches); 我得到Array ( [0] => johndoe@ [1] => johndoe)

那么我需要如何更改正则表达式?

【问题讨论】:

  • 域单行:$domain = substr($email, strrpos($email, '@')+1);
  • 你会考虑改变你接受的答案吗?

标签: php regex email preg-match


【解决方案1】:
$parts = explode('@', "johndoe@example.com");

$user = $parts[0];
// Stick the @ back onto the domain since it was chopped off.
$domain = "@" . $parts[1];

【讨论】:

  • 更好,list($name,$_) = explode('@',$email); $domain = '@'.$_; - ideone.com/yHlz6
  • 正是我的想法。而且进行爆炸处理的成本更低。
  • @Brad Christie 在阅读了您看起来像 Perl 的评论后,有一瞬间我以为我将 Perl 问题解释为 PHP 问题 :)
  • @Michael:只是让人们猜测。我在临时解决方案中使用$_。无论好坏或无关紧要(并且有可读性的风险),它都可以更快地写出变量。而且,在我的记事本获得 PHP 编码的智能感知之前,我可能会继续这样做。 ;p
  • 一个电子邮件地址可以有多个“@”符号,如stackoverflow.com/questions/12355858/…中所述
【解决方案2】:

之前的一些答案是错误的,因为一个有效的电子邮件地址实际上可以包含多个 @ 符号,方法是将其包含在点分隔的引用文本中。请参阅以下示例:

$email = 'a."b@c".d@e.f';
echo (filter_var($email, FILTER_VALIDATE_EMAIL) ? 'V' : 'Inv'), 'alid email format.';

有效的电子邮件格式。


可以存在多个分隔的文本块和多个 @ 符号。这两个示例都是有效的电子邮件地址:

$email = 'a."b@c".d."@".e.f@g.h';
$email = '/."@@@@@@"./@a.b';

根据 Michael Berkowski 的爆炸式回答,此电子邮件地址如下所示:

$email = 'a."b@c".d@e.f';
$parts = explode('@', $email);
$user = $parts[0];
$domain = '@' . $parts[1];

用户:a."b"
域:@c".d


使用此解决方案的任何人都应注意潜在的滥用。根据这些输出接受电子邮件地址,然后将 $email 插入数据库可能会产生负面影响。

$email = 'a."b@c".d@INSERT BAD STUFF HERE';

只要先用filter_var进行验证,这些函数的内容才是准确的。

从左到右:

这是一个简单的非正则表达式、非爆炸性解决方案,用于查找不包含在分隔和引用的文本中的第一个 @。根据 filter_var,嵌套的分隔文本被认为是无效的,因此找到正确的 @ 是一个非常简单的搜索。

if(filter_var($email, FILTER_VALIDATE_EMAIL)) {
    $a = '"';
    $b = '.';
    $c = '@';
    $d = strlen($email);
    $contained = false;
    for($i = 0; $i < $d; ++$i) {
        if($contained) {
            if($email[$i] === $a && $email[$i + 1] === $b) {
                $contained = false;
                ++$i;
            }
        }
        elseif($email[$i] === $c)
            break;
        elseif($email[$i] === $b && $email[$i + 1] === $a) {
            $contained = true;
            ++$i;
        }
    }
    $local = substr($email, 0, $i);
    $domain = substr($email, $i);
}

这是隐藏在函数中的相同代码。

function parse_email($email) {
    if(!filter_var($email, FILTER_VALIDATE_EMAIL)) return false;
    $a = '"';
    $b = '.';
    $c = '@';
    $d = strlen($email);
    $contained = false;
    for($i = 0; $i < $d; ++$i) {
        if($contained) {
            if($email[$i] === $a && $email[$i + 1] === $b) {
                $contained = false;
                ++$i;
            }
        }
        elseif($email[$i] === $c)
            break;
        elseif($email[$i] === $b && $email[$i + 1] === $a) {
            $contained = true;
            ++$i;
        }
    }
    return array('local' => substr($email, 0, $i), 'domain' => substr($email, $i));
}

使用中:

$email = 'a."b@c".x."@".d.e@f.g';
$email = parse_email($email);
if($email !== false)
    print_r($email);
else
    echo 'Bad email address.';

数组([本地] => a."b@c".x."@".d.e [域] => @f.g)

$email = 'a."b@c".x."@".d.e@f.g@';
$email = parse_email($email);
if($email !== false)
    print_r($email);
else
    echo 'Bad email address.';

错误的电子邮件地址。


从右边:

在对 filter_var 进行了一些测试并研究了可以接受的有效域名(Hostnames 由点分隔)之后,我创建了这个函数以获得更好的性能。在有效的电子邮件地址中,最后一个 @ 应该是真正的 @,因为 @ 符号不应出现在有效电子邮件地址的域中。

if(filter_var($email, FILTER_VALIDATE_EMAIL)) {
    $domain = strrpos($email, '@');
    $local = substr($email, 0, $domain);
    $domain = substr($email, $domain);
}

作为一个函数:

function parse_email($email) {
    if(!filter_var($email, FILTER_VALIDATE_EMAIL)) return false;
    $a = strrpos($email, '@');
    return array('local' => substr($email, 0, $a), 'domain' => substr($email, $a));
}

或者使用explode和implode:

if(filter_var($email, FILTER_VALIDATE_EMAIL)) {
    $local = explode('@', $email);
    $domain = '@' . array_pop($local);
    $local = implode('@', $local);
}

作为一个函数:

function parse_email($email) {
    if(!filter_var($email, FILTER_VALIDATE_EMAIL)) return false;
    $email = explode('@', $email);
    $domain = '@' . array_pop($email);
    return array('local' => implode('@', $email), 'domain' => $domain);
}

如果您仍想使用正则表达式,从有效电子邮件地址的末尾开始拆分字符串是最安全的选择。

/(.*)(@.*)$/

(.*) 匹配任何内容。
(@.*) 匹配以 @ 符号开头的任何内容。
$ 字符串结束。

if(filter_var($email, FILTER_VALIDATE_EMAIL)) {
    $local = preg_split('/(.*)(@.*)$/', $email, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
    $domain = $local[1];
    $local = $local[0];
}

作为一个函数:

function parse_email($email) {
    if(!filter_var($email, FILTER_VALIDATE_EMAIL)) return false;
    $email = preg_split('/(.*)(@.*)$/', $email, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
    return array('local' => $email[0], 'domain' => $email[1]);
}

或者

if(filter_var($email, FILTER_VALIDATE_EMAIL)) {
    preg_match('/(.*)(@.*)$/', $email, $matches);
    $local = $matches[1];
    $domain = $matches[2];
}

作为一个函数:

function parse_email($email) {
    if(!filter_var($email, FILTER_VALIDATE_EMAIL)) return false;
    preg_match('/(.*)(@.*)$/', $email, $matches);
    return array('local' => $matches[1], 'domain' => $matches[2]);
}

【讨论】:

  • 我从未见过包含多个@符号的实际使用的电子邮件地址。我 1988 年的原始电子邮件地址中有一个 # 表示路由信息。当我阅读电子邮件中的 RFC 以了解什么是有效的什么是无效的时,我感到很惊讶。不过,有些东西我在生产中从未见过。
【解决方案3】:

使用 explode 可能是这里最好的方法,但要使用正则表达式,您可以这样做:

/^([^@]*)(@.*)/

^ 字符串开头

([^@]*) 任何不是@符号的东西 ($matches[0])

(@.*) @ 符号后跟任何内容 ($matches[1])

【讨论】:

    【解决方案4】:

    回答

    $parts = explode("@", $email);
    $domain = array_pop($parts);
    $name = implode("@",$parts);
    

    这解决了 Brogan 的两种极端情况(a."b@c".d."@".e.f@g.h/."@@@@@@"./@a.b),正如您在this Ideone 中看到的那样


    currently accepted answer 无效,因为有多个“@”大小写。

    我喜欢@Brogan's answer,直到我读到他的最后一句话:

    在有效的电子邮件地址中,最后一个 @ 应该是真正的 @,因为 @ 符号不应出现在有效电子邮件地址的域中。

    this other answer 支持。如果这是真的,那么他的回答就显得过于复杂了。

    【讨论】:

    • ..downvotes?为什么?我不知道我的解决方案有什么问题......据我所知,它有效。如果不是,请告诉我为什么! D:
    【解决方案5】:

    如果你想要一个 preg_match 解决方案,你也可以这样做

    preg_match('/([^<]+)(@[^<]+)/','johndoe@example.com',$matches);
    

    【讨论】:

      【解决方案6】:

      使用正则表达式。例如:

      $mailadress = "email@company.com";     
      $exp_arr= preg_match_all("/(.*)@(.*)\.(.*)/",$mailadress,$newarr, PREG_SET_ORDER); 
      
      /*
      Array output:
      Array
      (
          [0] => Array
              (
                  [0] => email@company.com
                  [1] => email
                  [2] => company
                  [3] => com
              )
      
      )
      */
      

      【讨论】:

      • 这匹配无效的@@@@@@@@@.,但不匹配有效的me@localhost
      【解决方案7】:

      我为此创建了一个通用正则表达式 validates 并创建了完整电子邮件、用户和域的命名捕获。

      正则表达式:

      (?<email>(?<mailbox>(?:\w|[!#$%&'*+/=?^`{|}~-])+(?:\.(?:\w|[!#$%&'*+/=?^`{|}~-])+)*)@(?<full_domain>(?<subdomains>(?:(?:[^\W\d_](?:(?:[^\W_]|-)+[^\W_])?)\.)*)(?<root_domain>[^\W\d_](?:(?:[^\W_]|-)+[^\W_])?)\.(?<tld>[^\W\d_](?:(?:[^\W_]|-)+[^\W_])?)))
      

      说明:

      (?<email>                          #  start Full Email capture
        (?<mailbox>                      #    Mailbox
          (?:\w|[!#$%&'*+/=?^`{|}~-])+   #      letter, number, underscore, or any of these special characters
          (?:                            #      Group: allow . in the middle of mailbox; can have multiple but can't be consecutive (no john..smith)
            \.                           #        match "." 
            (?:\w|[!#$%&'*+/=?^`{|}~-])+ #        letter, number, underscore, or any of these special characters
          )*                             #      allow one letter mailboxes
        )                                #    close Mailbox capture
        @                                #    match "@"
        (?<full_domain>                  #    Full Domain (including subdomains and tld)
          (?<subdomains>                 #      All Subdomains
            (?:                          #        label + '.' (so we can allow 0 or more)
              (?:                        #          label text
                [^\W\d_]                 #            start with a letter (\W is the inverse of \w so we end up with \w minus numbers and _)
                (?:                      #            paired with a ? to allow single letter domains
                  (?:[^\W_]|-)+          #              allow letters, numbers, hyphens, but not underscore
                  [^\W_]                 #              if domain is more than one character, it has to end with a letter or digit (not a hyphen or underscore)
                )?                       #            allow one letter sub domains
              )                          #          end label text
            \.)*                         #        allow 0 or more subdomains separated by '.'
          )                              #      close All Subdomains capture
          (?<root_domain>                #      Root Domain
            [^\W\d_]                     #        start with a letter
            (?:                          #        paired with ? to make characters after the first optional
              (?:[^\W_]|-)+              #          allow letters, numbers, hyphens
              [^\W_]                     #          if domain is more than one character, it has to end with a letter or digit (not a hyphen or underscore)
            )?                           #        allow one letter domains
          )                              #      close Root Domain capture
          \.                             #      separator
          (?<tld>                        #      TLD
            [^\W\d_]                     #        start with a letter
            (?:                          #        paired with ? to make characters after the first optional
              (?:[^\W_]|-)+              #          allow letters, numbers, hyphens
              [^\W_]                     #          if domain is more than one character, it has to end with a letter or digit (not a hyphen)
            )?                           #        allow single letter tld
          )                              #      close TLD capture
        )                                #    close Full Domain capture
      )                                  #  close Full Email capture
      

      注意事项

      通用正则表达式:我发布的只是正则表达式搜索本身,而不是 php 独有的东西。这是为了让其他根据名称“Regex Split Email Address”找到它的人更容易使用。

      功能兼容性:并非所有正则表达式处理器都支持命名捕获,如果您遇到问题,请使用Regexr 上的文本对其进行测试(查看详细信息以查看捕获)。如果它在那里工作,请仔细检查您使用的正则表达式引擎是否支持命名捕获。

      域 RFC: 域部分也是基于 domain RFC 而不仅仅是 2822

      危险字符:我已明确包含'$! 等,以明确这些 mail RFC 允许的,并且如果由于特殊的处理要求(例如阻止可能的 sql 注入攻击),您的系统应禁止特定字符集

      无转义:对于邮箱名称我只包含点原子格式,我有意排除了点或斜线转义支持

      Subtle Letters:对于某些部分,我使用 [^\W\d_] 而不是 [a-zA-Z] 来改进对英语以外语言的支持。

      越界:由于某些系统中捕获组处理的特殊性,我使用+ 代替{,61}。如果您在可能容易受到缓冲区溢出攻击的地方使用它,请记住bound your inputs

      致谢:改编自 Tripleaxis 的社区帖子,该帖子又取自 .net 帮助文件

      【讨论】:

      • 注意:-1 来自另一个回答者,他对我为什么包含 '$! 感到困惑。我已经更新了答案,以便更清楚他们为什么在那里。如果您觉得有用,请随时查看并评价自己。
      猜你喜欢
      • 2011-05-20
      • 1970-01-01
      • 1970-01-01
      • 2012-01-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多