Regex拆分电子邮件地址


Regex split email address

我需要一些帮助php regex,我想"拆分"电子邮件地址"johndoe@example.com"到"johndoe"answers"@example.com"

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

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

$parts = explode('@', "johndoe@example.com");
$user = $parts[0];
// Stick the @ back onto the domain since it was chopped off.
$domain = "@" . $parts[1];

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

$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.';

Array ([local] => 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进行了一些测试并研究了什么是可接受的有效域名(主机名以点分隔)之后,我创建了这个函数以获得更好的性能。在一个有效的电子邮件地址中,最后一个@应该是真正的@,因为@符号不应该出现在一个有效的电子邮件地址的域中。

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

或者使用explosion and 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]);
}

使用爆炸可能是这里最好的方法,但要使用regex,您将做这样的事情:

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

^ string

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

(@ *)。 @符号紧随其后的是什么($ matches [1])

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

这解决了Brogan的边界情况(a."b@c".d."@".e.f@g.h/."@@@@@@"./@a.b),你可以在这个Ideone

中看到

由于多个"@"大小写,当前接受的答案无效。

我很喜欢@Brogan的回答,直到我读到他的最后一句话:

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

得到另一个答案的支持。如果这是真的,他的回答似乎没有必要那么复杂。

如果需要preg_match解决方案,还可以这样做

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

使用正则表达式。例如:

$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
        )
)
*/

我已经为此创建了一个通用正则表达式,用于验证和创建完整电子邮件、用户和域的命名捕获。

正则表达式:

(?<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拆分电子邮件地址"找到它。

特性兼容性:不是所有的regex处理器都支持命名捕获,如果你在Regexr上遇到麻烦,用你的文本测试它(检查细节以查看捕获)。如果它在那里工作,然后仔细检查你正在使用的regex引擎是否支持命名捕获。

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

危险字符:我已经明确地包含了'$!等,以明确这些是邮件RFC允许的,并且如果由于特殊的处理要求(如阻止可能的sql注入攻击),在您的系统中不允许一组特定的字符,则可以轻松删除

No Escape:对于邮箱名我只包含了点原子格式,我故意排除了点或斜杠转义支持

微妙的字母:对于某些部分,我使用[^'W'd_]而不是[a-zA-Z]来提高对英语以外语言的支持。

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

credit:从Tripleaxis的社区帖子修改,这是反过来从。net帮助文件

相关文章: