用PHP解析电子邮件地址?

5
类似于这个问题,我应该如何解析这种格式的电子邮件地址:
"Bob Smith" <bob@company.com>, joe@company.com, "John Doe"<john@company.com>

并获得以下结果:
array(
    'bob@company.com'=>'Bob Smith'
    'joe@company.com'=>''
    'john@company.com'=>'John Doe'
);

可能是解析RFC 822兼容的收件人地址的重复问题。 - mario
6个回答

12

7
这个应该适用于几乎所有东西:
$str = '"Bob Smith" <bob@company.com>, joe@company.com, "John Doe"<john@company.com>, Billy Doe<billy@company.com>';
$emails = array();

if(preg_match_all('/\s*"?([^><,"]+)"?\s*((?:<[^><,]+>)?)\s*/', $str, $matches, PREG_SET_ORDER) > 0)
{
    foreach($matches as $m)
    {
        if(! empty($m[2]))
        {
            $emails[trim($m[2], '<>')] = $m[1];
        }
        else
        {
            $emails[$m[1]] = '';
        }
    }
}

print_r($emails);

结果:

Array
(
    [bob@company.com] => Bob Smith
    [joe@company.com] => 
    [john@company.com] => John Doe
    [billy@company.com] => Billy Doe
)

2
我喜欢它的简洁性,但它不能处理名称部分中的逗号,例如'"Smith,Bob" bob@example.com'。 - dlo

1
以下是一段完全可用的代码,甚至可以验证电子邮件地址是否正确 ;)
<?php
$mails = '"Bob Smith" <bob@company.com>, joe@company.com, "John Doe"<john@company.com>';

$records = explode(",",$mails);

foreach($records as $r){
  preg_match("#\"([\w\s]+)\"#",$r,$matches_1);
  $name = $matches_1[1];


  preg_match("/[^0-9<][A-z0-9_]+([.][A-z0-9_]+)*[@][A-z0-9_]+([.][A-z0-9_]+)*[.][A-z]{2,4}/i",$r,$matches_2);
  $email = $matches_2[0];

  echo "Name: $name <br /> Email: $email <br />";
}

?>

[A-z] 包括以下符号:[, \, ], ^, _ 和反引号。您不需要包含这些符号。 - mickmackusa
在模式中双引号前的斜杠转义可以通过在单引号字符串内声明模式来避免。@对于正则表达式引擎没有特殊含义,不需要通过大括号显式地写成文字。 [0-9A-Za-z_] 更简单地写为 \w。与其在循环中分割然后调用一对正则表达式调用,不如只调用一次 preg_match_all() 然后迭代该有效负载。 - mickmackusa
在模式中,在双引号之前转义斜杠可以通过在单引号字符串内声明模式来避免。@对正则表达式引擎没有特殊含义,不需要显式地写成字面量形式。[0-9A-Za-z_]可以更简单地写为\w。不必在循环中拆分然后调用一对正则表达式调用,只需调用一次preg_match_all()并迭代该有效负载即可。 - mickmackusa

1
对于类似的任务,我使用了以下正则表达式:

\s*(?:"([^"]*)"|([^,""<>]*))?\s*(?:(?:,|<|\s+|^)([^<@\s,]+@[^>@\s,]+)>?)\s*

https://regex101.com/r/Lpsjmr/1

PHP代码:

$str = '"Bob Smith" <bob@company.com>, joe@company.com, "John Doe"<john@company.com>, Billy Doe<billy@company.com>';
if (preg_match_all('/\s*(?:"([^"]*)"|([^,""<>]*))?\s*(?:(?:,|<|\s+|^)([^<@\s,]+@[^>@\s,]+)>?)\s*/', $str, $matches, PREG_SET_ORDER) > 0) {
    $matches = array_map(function($x) { return [$x[1] . $x[2], $x[3]]; }, $matches);
    print_r($matches);
}

否定字符类为什么要两次包含双引号? - mickmackusa

0
对于@mario答案中未正确解析的输入字符串,使用“分支重置”((?|...))和捕获组((...))来解析逗号之间的子字符串。

分支重置确保(可选出现的)名称始终存储在匹配数组的第一列1中,而电子邮件始终存储在第二列2中。

代码:(演示

$emails = '"Bob Smith" <bob@company.com>, joe@company.com, "John Doe"<john@company.com>, Billy Doe<billy@company.com>';

preg_match_all('/(?|(?|"([^"]+)"|([^<@]+)) ?<(.+?)>|()(.+?))(?:$|, ?)/', $emails, $matches, PREG_SET_ORDER);
var_export(
    array_column($matches, 1, 2)
);

0
  1. 按逗号拆分字符串
  2. 如果是有效的电子邮件,则存储它,否则
    1. 去除“>”字符的右边空格
    2. 按“<”拆分字符串
    3. 修剪字符串(包括“"”和空格)

我本想说这行不通,但我认为它会很好地运行!开始编码。 - John Ballinger
如果你想获取姓名和电子邮件,这种方法可能不太适用。因为一个名字可能是"Ballinger, John john@example.com, email 2",但如果只是获取电子邮件,这个方法还不错。(我想要姓名和电子邮件,但无法安装PHP扩展。) - John Ballinger

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