在PHP中拆分名字和姓氏的最佳方法

时间:2012-01-10 18:58:54

标签: php

我遇到了NAME字段,其格式通常为:

FirstName LastName

但是,我也偶尔会出现这些格式的任何名称(带前缀或后缀):

Mr. First Last
First Last Jr.

人们认为在PHP中将这些变量分割为FIRST / LAST名称变量的安全方法是什么?我无法想出任何一直在工作的东西......

16 个答案:

答案 0 :(得分:22)

正则表达式是处理这类事情的最佳方式。 试试这件 - 它会拉出前缀,名字,姓氏和后缀:

$array = array(
    'FirstName LastName',
    'Mr. First Last',
    'First Last Jr.',
    'Shaqueal O’neal',
    'D’angelo Hall',
);

foreach ($array as $name)
{
    $results = array();
    echo $name;
    preg_match('#^(\w+\.)?\s*([\'\’\w]+)\s+([\'\’\w]+)\s*(\w+\.?)?$#', $name, $results);
print_r($results);
}

结果如下:

FirstName LastName
Array
(
    [0] => FirstName LastName
    [1] => 
    [2] => FirstName
    [3] => LastName
)
Mr. First Last
Array
(
    [0] => Mr. First Last
    [1] => Mr.
    [2] => First
    [3] => Last
)
First Last Jr.
Array
(
    [0] => First Last Jr.
    [1] => 
    [2] => First
    [3] => Last
    [4] => Jr.
)
shaqueal o’neal
Array
(
    [0] => shaqueal o’neal
    [1] => 
    [2] => shaqueal
    [3] => o’neal
)
d’angelo hall
Array
(
    [0] => d’angelo hall
    [1] => 
    [2] => d’angelo
    [3] => hall
)

等...

所以在数组中 $array[0]包含整个字符串。 $array[2]始终是名字,$array[3]始终是姓氏。 $array[1]是前缀,$array[4](并不总是设置)是后缀。 我还添加了代码来处理像Shaqueal O'neal和D'angelo Hall这样的名字的'和'。

答案 1 :(得分:16)

接受的答案不适用于英语以外的语言或“Oscar de la Hoya”等名称。

我认为这样做是我认为utf-8安全并且适用于所有这些案例,建立在已接受的答案假设前缀和后缀将具有句号的基础上:

/**
 * splits single name string into salutation, first, last, suffix
 * 
 * @param string $name
 * @return array
 */
public static function doSplitName($name)
{
    $results = array();

    $r = explode(' ', $name);
    $size = count($r);

    //check first for period, assume salutation if so
    if (mb_strpos($r[0], '.') === false)
    {
        $results['salutation'] = '';
        $results['first'] = $r[0];
    }
    else
    {
        $results['salutation'] = $r[0];
        $results['first'] = $r[1];
    }

    //check last for period, assume suffix if so
    if (mb_strpos($r[$size - 1], '.') === false)
    {
        $results['suffix'] = '';
    }
    else
    {
        $results['suffix'] = $r[$size - 1];
    }

    //combine remains into last
    $start = ($results['salutation']) ? 2 : 1;
    $end = ($results['suffix']) ? $size - 2 : $size - 1;

    $last = '';
    for ($i = $start; $i <= $end; $i++)
    {
        $last .= ' '.$r[$i];
    }
    $results['last'] = trim($last);

    return $results;
}

这是phpunit测试:

public function testDoSplitName()
{
    $array = array(
        'FirstName LastName',
        'Mr. First Last',
        'First Last Jr.',
        'Shaqueal O\'neal',
        'D’angelo Hall',
        'Václav Havel',
        'Oscar De La Hoya',
        'АБВГҐД ЂЃЕЀЁЄЖЗ', //cyrillic
        'דִּיש מַחֲזֹור', //yiddish
    );

    $assertions = array(
            array(
                    'salutation' => '',
                    'first' => 'FirstName',
                    'last' => 'LastName',
                    'suffix' => ''
                ),
            array(
                    'salutation' => 'Mr.',
                    'first' => 'First',
                    'last' => 'Last',
                    'suffix' => ''
                ),
            array(
                    'salutation' => '',
                    'first' => 'First',
                    'last' => 'Last',
                    'suffix' => 'Jr.'
                ),
            array(
                    'salutation' => '',
                    'first' => 'Shaqueal',
                    'last' => 'O\'neal',
                    'suffix' => ''
                ),
            array(
                    'salutation' => '',
                    'first' => 'D’angelo',
                    'last' => 'Hall',
                    'suffix' => ''
                ),
            array(
                    'salutation' => '',
                    'first' => 'Václav',
                    'last' => 'Havel',
                    'suffix' => ''
                ),
            array(
                    'salutation' => '',
                    'first' => 'Oscar',
                    'last' => 'De La Hoya',
                    'suffix' => ''
                ),
            array(
                    'salutation' => '',
                    'first' => 'АБВГҐД',
                    'last' => 'ЂЃЕЀЁЄЖЗ',
                    'suffix' => ''
                ),
            array(
                    'salutation' => '',
                    'first' => 'דִּיש',
                    'last' => 'מַחֲזֹור',
                    'suffix' => ''
                ),
        );

    foreach ($array as $key => $name)
    {
        $result = Customer::doSplitName($name);

        $this->assertEquals($assertions[$key], $result);
    }
}

答案 2 :(得分:5)

你找不到一种安全的方法来解决这个问题,即使是人类总是可以告诉哪些部分属于名字,哪些部分属于姓氏,特别是当其中一个部分包含多个单词时: Andrea Frank Gutenberg 。中间部分 Frank 可以是第二个名字或姓氏为 Gutenberg 的姓氏。

您可以做的最好的事情是,为firstname和lastname提供不同的输入字段,并在数据库中将它们分开安全,这样可以避免很多问题。

答案 3 :(得分:2)

还有另一种解决方案:

// First, just for safety make replacement '.' for '. '
$both = str_replace('.', '. ', $both);

// Now delete titles
$both = preg_replace('/[^ ]+\./', '', $both);

// Delete redundant spaces
$both = trim(str_replace('  ', ' ', $both));

// Explode
$split = explode(" ", $both, 2);
if( count($split) > 1 ) {
    list($name, $surname) = $split;
} else {
    $name = $split[0];
    $surname = '';
}

答案 4 :(得分:2)

这不是一个简单的问题,在很大程度上,你获得可行解决方案的能力取决于文化“规范”

  1. 首先使用preg_replace例如

    关闭任何“敬语”
     $normalized_name = preg_replace('/^(Mr\.*\sJustice|Mr\.*\s+|Mrs\.*\s+|Ms\.\s+|Dr\.*\s+|Justice|etc.)*(.*)$/is', '$2', trim($input_name));
    
  2. 下一个hive off off任何尾随后缀

    $normalized_name = preg_replace('/^(.*)(Jr\.*|III|Phd\.*|Md\.)$/is', '$1', $normalized_name);
    
  3. 最后在第一个空白处拆分以获得名字和姓氏。

  4. 显然,仅在“英语”中就有许多可能的敬意,我想不出太多的后缀,但可能比我列出的更多。

答案 5 :(得分:2)

这里的大图书馆到目前为止已经完美地解析了名字: https://github.com/joshfraser/PHP-Name-Parser

答案 6 :(得分:1)

首先爆炸FIRST / LAST,然后连接前缀
上面的例子:

  

Vicent van Gogh

firstname 是数组的第一个索引。 在firstname之后,是 lastname ,所以你只需要获取其余的数组索引。

之后,连接前缀/ sufix。

  

先生。梵高副总统,副梵高,

答案 7 :(得分:0)

假设您不关心Mr.或Jr.部分且$text包含名称:

$textarray = explode(" ", $text);

foreach($textarray as $key => $value)
{
    if (preg_match("/\./", $value))
    {
        unset($text[$key]);
    }
}

$first_last = array_values($text);

$firstname = $first_last[0];
$lastname = $first_last[1];

$firstname将成为名字,$lastname将成为姓氏。不是最干净的方式,但这是一种可能性。

答案 8 :(得分:0)

如果您有数据库,我会创建一个名为prefix和suffix的列。然后运行查询以从文本中提取该部分。

UPDATE names SET prefix = 'mr.' WHERE name LIKE 'mr. %'
UPDATE names SET name = substring(name, 4) WHERE name LIKE 'mr. %'

通过这种方式,您可以在数据库中保留不同的前缀,它就像一个魅力,因为它是一个批处理语句,您可以根据需要为扫描添加尽可能多的后缀或前缀,而且构建时间不长。

然后,您可以通过这种方式删除所有前缀和后缀后拆分第一个空格。

答案 9 :(得分:0)

另一种解决方案:

function getFirstLastName($fullName) {
    $fullName = $firstLast = trim($fullName);
    if (preg_match('/\s/', $fullName)) {
        $first = mb_substr($fullName, 0, mb_strpos($fullName, " "));
        $last = mb_substr($fullName, -abs(mb_strpos(strrev($fullName), " ")));
        $firstLast = $first . " " . $last;
    }
    return $firstLast;
}

希望这很有用!

答案 10 :(得分:0)

不要拆分名称。始终将人名完整存储;如果你想使用更短的东西,请添加“我们应该给你打电话什么?”字段。

原因:您无法可靠地拆分名称。不同的国家无论如何都将他们的名字命名为不同的命令(例如在法国,姓氏通常是第一位的;在一些远东国家也是如此,但你不能用语言来检测这一点,因为来自这些国家的移民经常互换他们的名称以避免混淆......但不是所有 emigres。)

有些国家根本没有预期的名称结构;例如在俄罗斯和冰岛,人们仍然使用赞助人而不是姓氏。

即使在英语中,也有人有双管姓氏,没有连字符;然后有人用Mac,Mc,De,de,Van,van和其他前缀词作为他们名字的一部分。只是为了忽略问题并提出更明智的问题, 更好。

如果您被迫分割名称,例如信用卡处理,我会选择一些简单的东西,比如在最后一个空格处分裂,而不是试图变得聪明并使分割正确。卡片公司如果进行分割,就更有可能使用这种天真的方法,并且目标是匹配他们可能的行为。 请注意只允许拆分名称的接口。

答案 11 :(得分:0)

我总是建议尽可能多地从用户捕获独立数据,同时只需要功能正常工作所需的数据。使用此方法允许多种格式和名称构建方案。

在最终用户级别独立捕获以下字段可能会消除解析的需要,或者至少解除特殊字符或拆分名称的解析问题,例如......“圣约翰” ,“de la Hoya”和“Jr. III”。

  • 称呼(例如先生,女士,博士等
  • givenname(例如John,Mary-Catherine,Mary Lou等
  • 中间名(例如戴维斯,Alysia-Anne,D'Marco等
  • 姓氏(例如de la Hoya,Smith-Peters,St。John等
  • 后缀(例如,Sr.,Jr。,Jr。III等

一旦捕获,这些名称可以在程序员或最终用户(程序员提供的选项)认为合适时动态重新排列,构建或格式化。

答案 12 :(得分:0)

这是我使用正则表达式的函数。可以轻松扩展其他规则,例如更多学术头衔

function names($name) {
    $replace = [
        '/[,:]/' => '',
        // Academic degrees Czech Republic
        '/(doc\.|Ing\.|Ph\.D\.|Bc\.|Dr\.|RNDr\.|PhDr\.|JUDr\.|MDDr\.|MVDr\.|DiS\.|Dr\.|prof\.)/i' => '',
        // Academic degrees USA
        '/(B\.A\.|B\.S\.|M\.A\.|M\.S\.|Ed\.D\.|Ph\.D\.)/i' => '',
        '/^(mr|mrs|mrs|miss|sr|sir)\.? /i' => '',
        '/ (jr|sr)\.?$/i' => '',
        // multi spaces, new lines etc.
        '/\s+/mu' => ' ',
    ];
    $n = preg_replace(array_keys($replace), $replace, trim($name));
    if (strpos($n, ' ') !== false) {
        $names = preg_split('/[\s,]+/', trim($n));
        return ['first' => reset($names), 'last' => end($names)];
    }
}

这是一些测试:

foreach (
    [
        'Robert Downey Jr.',
        'Billy Bob Thornton',
        'John O\'Shea',
        'Sir Nicholas George Winton',
        'Billy el Niño',
        'Mr. Bean',
        'Miss Eve Moneypenny',
        'Miss Moneypenny',
        'D’angelo Hall',
        'Garry        Longhurst    Spaces',
        'doc. Ing. Ota Plk, Ph.D.',
        'J. J. Abrams',
        'Bruce A Johnson',
    ] as $name
) {
    echo 'Name: ' . $name . PHP_EOL . var_export(names($name), true) . PHP_EOL . str_repeat('-', 35) . PHP_EOL;
}

和结果:

Name: Robert Downey Jr.
array (
  'first' => 'Robert',
  'last' => 'Downey',
)
-----------------------------------
Name: Billy Bob Thornton
array (
  'first' => 'Billy',
  'last' => 'Thornton',
)
-----------------------------------
Name: John O'Shea
array (
  'first' => 'John',
  'last' => 'O\'Shea',
)
-----------------------------------
Name: Sir Nicholas George Winton
array (
  'first' => 'Nicholas',
  'last' => 'Winton',
)
-----------------------------------
Name: Billy el Niño
array (
  'first' => 'Billy',
  'last' => 'Niño',
)
-----------------------------------
Name: Mr. Bean
NULL
-----------------------------------
Name: Miss Eve Moneypenny
array (
  'first' => 'Eve',
  'last' => 'Moneypenny',
)
-----------------------------------
Name: Miss Moneypenny
NULL
-----------------------------------
Name: D’angelo Hall
array (
  'first' => 'D’angelo',
  'last' => 'Hall',
)
-----------------------------------
Name: Garry        Longhurst    Spaces
array (
  'first' => 'Garry',
  'last' => 'Spaces',
)
-----------------------------------
Name: doc. Ing. Ota Plk, Ph.D.
array (
  'first' => 'Ota',
  'last' => 'Plk',
)
-----------------------------------
Name: J. J. Abrams
array (
  'first' => 'J.',
  'last' => 'Abrams',
)
-----------------------------------
Name: Bruce A Johnson
array (
  'first' => 'Bruce',
  'last' => 'Johnson',
)
-----------------------------------

答案 13 :(得分:0)

关于这一个的例子 - 没有正则表达式:

function explode_name($name)
{
    $honorifics = "Mr. Mister Mrs. Misses Ms. Miss Mademoiselle Mlle Madam Fräulein Justice Sir. Dr. Lady Lord";
    $lastname_prefixes = "Van Von Mc";
    $suffixes = "Sr. Snr. Jr. Jnr. I II III IV V PhD PhyD Ph.D. AB A.B. BA B.A. BE B.E. B.F.A. BS B.S. B.Sc. MS M.S. M.Sc. MFA M.F.A. MBA M.B.A. JD J.D. MD M.D. DO D.O. DC D.C. EdD Ed.D. D.Phil. DBA D.B.A. LLB L.L.B. LLM L.L.M. LLD L.L.D. CCNA OBE MMFT DMFT MSC MSW DSW MAPC MSEd LPsy LMFT LCSW LMHC LCMHC CMHC LMSW LPCC LPC LCPC LPC-S LCAT";
    $name_parts = explode(' ', $name);
    $name_array = ['honorific'=>'', 'first'=>'', 'middle'=>'', 'last'=>'', 'suffix'=>''];

    // Look for Honorifics
    if (stripos($honorifics, $name_parts[0]) !== false)
    {
        // Shift the honorific off the front of the name_parts array.
        // This also has the effect that the honorific isn't there to
        // confuse things later.
        $name_array['honorific'] = array_shift($name_parts);
    }

    // Look for name suffixes
    if (stripos($suffixes, $name_parts[count($name_parts)-1]) !== false)
    {
        // Pop the suffix off the end of the name_parts array, with the
        // added benifit that the suffix won't be there to muck things 
        // up later on.
        $name_array['suffix'] = array_pop($name_parts);
    }

    $num_parts = count($name_parts);

    if ($num_parts == 0)
    {
        $name_array['first'] = $name;
        return $name_array;
    }
    else if ($num_parts == 1)
    {
        $name_array['first'] = $name;
        return $name_array;
    }
    else if ($num_parts == 2)
    {
        $name_array['first'] = $name_parts[0];
        $name_array['last'] = $name_parts[1];
        return $name_array;
    }
    else if ($num_parts == 3)
    {
        // Well then, things are a bit more dodgy, what?
        if (stripos("LLC Inc Store", $name_parts[2]) !== false)
        {
            // Then we assume this ia a business name, so put it all in the
            // first name
            $name_array['first'] = $name;
            return $name_array;
        }
        else if (stripos($lastname_prefixes, $name_parts[1]) !== false)
        {
            // Assume the last two parts are all part of the last name (and
            // there's no middle name
            $name_array['first'] = $name_parts[0];
            $name_array['last'] = $name_parts[1].' '.$name_parts[2];
            return $name_array;            
        }
        else
        {
            // Assume it's a first, middle, last affair
            $name_array['first'] = $name_parts[0];
            $name_array['middle'] = $name_parts[1];
            $name_array['last'] = $name_parts[2];
            return $name_array;            
        }
    }
    else
    {
        if (stripos($lastname_prefixes, $name_parts[2]) !== false)
        {
            // Assume it's a first, middle, last with one of those two part
            // last names.
            $name_array['first'] = $name_parts[0];
            $name_array['middle'] = $name_parts[1];
            // Concantinate the rest (returning the stripped out spaces) 
            // into the last name.
            for ($i=2; $i<$num_parts; ++$i)
            {
                $name_array['last'] .= $name_parts[$i].' ';
            }
            trim($name_array['last']);  // Trim off that trailing space
            return $name_array;            
        }
        else
        {
            // Not sure what is going on, so just put it all in the first name!
            $name_array['first'] = $name;
            return $name_array;
        }
    }
}

测试代码:

<table>
<tr><th>Full Name</th><th>Honorific</th><th>First</th><th>Middle</th>
    <th>Last</th><th>Suffix</th></th></tr>

<?php

$names = [
    "Gorzik von Gribblesnatch",
    "Dr. Philip Plimpton",
    "Dr Phil Dorselfin",
    "Reginald Klompkite III",
    "Dumpquip Higganog PhD",
    "SlumpGlum Muganerk",
    "Mr. Poon Noon",
    "Sir Geldin Blotchflooper",
    "Betsy Burger MMFT",
    "Dr. Grodd Mc Doogle",
    "Dr. Wilken Mc Dermott II",
    "Karen Debbie Donk",
    "Ferg Fleerper Fiddlenonk IV",
    "Quinten K. Flonk",
    "Dr Klonk Xiggle Bronhopper PhD",
    "Dr Blenton Flupp Yonkflibber",
];

foreach ($names as $name)
{
    echo "<tr>\n";
    $name_ex = explode_name($name);
    echo "<td>$name</td><td>{$name_ex['honorific']}</td><td>{$name_ex['first']}</td><td>{$name_ex['middle']}</td><td>{$name_ex['last']}</td><td>{$name_ex['suffix']}</td>\n";
    echo "</tr>\n";
}
?>
</table>    

结果:

&#13;
&#13;
    table {
        background-color: #ccc;
        border: 2px solid black;
    }
    td, th {
        padding: 4px 8px;
    }
    td {
        background-color: #0ff;
    }
&#13;
<table>
            <tr><th>Full Name</th><th>Honorific</th><th>First</th><th>Middle</th><th>Last</th><th>Suffix</th></th></tr>

<tr>
<td>Gorzik von Gribblesnatch</td><td></td><td>Gorzik</td><td></td><td>von Gribblesnatch</td><td></td>
</tr>
<tr>
<td>Dr. Philip Plimpton</td><td>Dr.</td><td>Philip</td><td></td><td>Plimpton</td><td></td>
</tr>
<tr>
<td>Dr Phil Dorselfin</td><td>Dr</td><td>Phil</td><td></td><td>Dorselfin</td><td></td>
</tr>
<tr>
<td>Reginald Klompkite III</td><td></td><td>Reginald</td><td></td><td>Klompkite</td><td>III</td>
</tr>
<tr>
<td>Dumpquip Higganog PhD</td><td></td><td>Dumpquip</td><td></td><td>Higganog</td><td>PhD</td>
</tr>
<tr>
<td>SlumpGlum Muganerk</td><td></td><td>SlumpGlum</td><td></td><td>Muganerk</td><td></td>
</tr>
<tr>
<td>Mr. Poon Noon</td><td>Mr.</td><td>Poon</td><td></td><td>Noon</td><td></td>
</tr>
<tr>
<td>Sir Geldin Blotchflooper</td><td>Sir</td><td>Geldin</td><td></td><td>Blotchflooper</td><td></td>
</tr>
<tr>
<td>Betsy Burger MMFT</td><td></td><td>Betsy</td><td></td><td>Burger</td><td>MMFT</td>
</tr>
<tr>
<td>Dr. Grodd Mc Doogle</td><td>Dr.</td><td>Grodd</td><td></td><td>Mc Doogle</td><td></td>
</tr>
<tr>
<td>Dr. Wilken Mc Dermott II</td><td>Dr.</td><td>Wilken</td><td></td><td>Mc Dermott</td><td>II</td>
</tr>
<tr>
<td>Karen Debbie Donk</td><td></td><td>Karen</td><td>Debbie</td><td>Donk</td><td></td>
</tr>
<tr>
<td>Ferg Fleerper Fiddlenonk IV</td><td></td><td>Ferg</td><td>Fleerper</td><td>Fiddlenonk</td><td>IV</td>
</tr>
<tr>
<td>Quinten K. Flonk</td><td></td><td>Quinten</td><td>K.</td><td>Flonk</td><td></td>
</tr>
<tr>
<td>Dr Klonk Xiggle Bronhopper PhD</td><td>Dr</td><td>Klonk</td><td>Xiggle</td><td>Bronhopper</td><td>PhD</td>
</tr>
<tr>
<td>Dr Blenton Flupp Yonkflibber</td><td>Dr</td><td>Blenton</td><td>Flupp</td><td>Yonkflibber</td><td></td>
</tr>
        </table>
&#13;
&#13;
&#13;

答案 14 :(得分:0)

如果您的 PHP版本> = 7.1 ,则可以使用array destructuring获取first_name last_name

[$first_name, $last_name] = explode(' ', $full_name);

答案 15 :(得分:-1)

如果您只是想通过以下方式拆分名称:

  • 一直到第一个&#34;空间&#34;字符为$ firstName
  • 第一个&#34;空间后的一切&#34;字符为$ lastName

你可以使用:

$firstName = substr($string, 0, strpos($string, ' '));
$lastName = substr($string, strlen($firstName));

它不是最复杂或文化敏感的方法,但它只有两行代码,并且通常可以在不需要高精度名称的项目上完成工作分裂。