4

Similar to this question, how could I parse e-mail addresses which are in this format,

"Bob Smith" <bob@company.com>, joe@company.com, "John Doe"<john@company.com>

And get the a result like this:

array(
    'bob@company.com'=>'Bob Smith'
    'joe@company.com'=>''
    'john@company.com'=>'John Doe'
);
Community
  • 1
  • 1
cwd
  • 53,018
  • 53
  • 161
  • 198
  • possible duplicate of [Parse RFC 822 compliant addresses in a TO header](http://stackoverflow.com/questions/6609195/parse-rfc-822-compliant-addresses-in-a-to-header) – mario Sep 29 '11 at 15:21

6 Answers6

12

Well, you could use mailparse_rfc822_parse_addresses(), which does exactly that. It's a PECL extension, so it might be easier to use Mail_RFC822::parseAddressList() as mentioned in the comments.

mario
  • 144,265
  • 20
  • 237
  • 291
7

This should work with just about anything:

$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);

Result:

Array
(
    [bob@company.com] => Bob Smith
    [joe@company.com] => 
    [john@company.com] => John Doe
    [billy@company.com] => Billy Doe
)
Luke
  • 13,678
  • 7
  • 45
  • 79
  • 2
    I like the simplicity, but it doesn't handle commas in the name part, like '"Smith, Bob" ' – dlo Jun 13 '13 at 15:47
1

For a similar task I've used the following regex:

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

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

PHP code:

$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);
}
1

This is a fully working piece of code below that even validates whether the email is correct or not ;)

<?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 />";
}

?>
Kenny
  • 5,350
  • 7
  • 29
  • 43
  • `[A-z]` includes the following symbols: `[`, `\`, `]`, `^`, `_`, and the back tick. You do not mean to include these. – mickmackusa Jan 01 '23 at 13:52
  • Escaping slashes before double quotes in the pattern can be avoided by declaring the pattern inside a single-quoted string. `@` does not have a special meaning to the regex engine and does not need to be explicitly written as a literal via braces. `[0-9A-Za-z_]` is more simply written as `\w`. Rather than splitting then calling pairs of regex calls in a loop, just call `preg_match_all()` once and iterate that payload. – mickmackusa Jan 01 '23 at 21:59
0

For input strings that are not properly parsed by the native function in @mario's answer, parse the substrings between delimiting commas using "branch resets" ((?|...)) and capture groups ((...)).

The branch reset ensure that (optionally occurring) names are always stored in column 1 of the matches array and that emails are always stored in column 2.

Code: (Demo)

$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)
);
mickmackusa
  • 43,625
  • 12
  • 83
  • 136
0
  1. Explode the string by comma
  2. If valid email then store it, if NO
    1. rtrim the '>' character
    2. explode by '<'
    3. trim the string for ('"', and ' ')
Tamás Pap
  • 17,777
  • 15
  • 70
  • 102
  • I wanted to say this wont work. But I think it will work fine! Starts coding. – John Ballinger Apr 30 '14 at 02:51
  • This doesnt work so well if you want to get the name & email. As a name could be "Ballinger, John , email 2" but for purely getting emails, its not bad. (I want name and email and cannot install the PHP extension.) – John Ballinger Apr 30 '14 at 02:58