Подтвердить что ты не робот

Лучший способ разделить имя и фамилию на PHP

Я застрял в поле NAME, которое обычно имеет формат:

FirstName LastName

Однако у меня также есть случайные имена, которые находятся в любом из этих форматов (с префиксом или суффиксом):

Mr. First Last
First Last Jr.

Что думают люди - это безопасный способ разделить их на переменные имени FIRST/LAST в PHP? Я не могу придумать что-нибудь, что имеет тенденцию работать все время...

4b9b3361

Ответ 1

Регулярное выражение - лучший способ справиться с чем-то подобным. Попробуйте этот кусок - он вытаскивает префикс, имя, фамилию и суффикс:

$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 Oneal и Dangelo Hall".

Ответ 2

Принятый ответ не работает для языков, отличных от английского, или таких имен, как "Оскар де ла Хойа".

Здесь я сделал что-то, что, по моему мнению, является 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);
    }
}

Ответ 3

Вы не найдете надежного способа решить эту проблему, даже человек всегда может сказать, какие части принадлежат первому и которые принадлежат к последнему, особенно когда один из них содержит несколько слов, таких как: Andrea Frank Gutenberg. Средняя часть Фрэнка может быть вторым первым именем или фамилией с девичью фамилией Гутенберг.

Лучшее, что вы можете сделать, - предоставить различные поля ввода для имени и фамилии и безопасно их разделять в базе данных, вы можете избежать множества проблем таким образом.

Ответ 4

Существует другое решение:

// 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 = '';
}

Ответ 5

Не простая проблема, и в значительной степени ваша способность получить работоспособное решение зависит от культурных "норм"

  • Сначала уберите любые "честности" - используя preg_replace например.

     $normalized_name = preg_replace('/^(Mr\.*\sJustice|Mr\.*\s+|Mrs\.*\s+|Ms\.\s+|Dr\.*\s+|Justice|etc.)*(.*)$/is', '$2', trim($input_name));
    
  • Затем уберите все суффиксные суффиксы

    $normalized_name = preg_replace('/^(.*)(Jr\.*|III|Phd\.*|Md\.)$/is', '$1', $normalized_name);
    
  • Наконец, разделим на первый пул, чтобы получить имя и фамилию.

Очевидно, что только в "английском" есть много возможных почтений, я не мог придумать слишком много суффиксов, но, вероятно, больше, чем я перечислял.

Ответ 7

Сначала вы взорвите FIRST/LAST, затем вы соедините префикс.

Пример выше:

Вичент ван Гог

firstname - это первый индекс массива. Что происходит после первого имени, это / lastname, поэтому вам просто нужно получить остальные индексы массива.

После этого вы объединяете префикс /sufix.

г. Vicent van Gogh
Vicent van Gogh jr.

Ответ 8

Предполагая, что вы не заботитесь о части мистера или младшего, и что $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 будет именем. Не самый чистый способ сделать это, но это возможность.

Ответ 9

Если у вас есть база данных, я бы создал столбец с именем prefix и suffix. Затем запустите запрос, чтобы извлечь эту часть из текста.

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

Таким образом, вы можете сохранить другой префикс в базе данных, он работает как шарм, потому что это пакетный оператор, и вы можете добавить столько же суффиксов или префикса к вашему сканированию, сколько захотите, и это не так долго.

Затем вы можете разбить на первое место после удаления всех префиксов и суффиксов таким образом.

Ответ 10

Другое решение:

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

Надеюсь, что это полезно!

Ответ 11

Не разделяйте имена. Всегда храните имена людей в полном объеме; если вы хотите использовать что-то более короткое, добавьте "Что мы должны будем вам звонить?" вместо этого.

Причина: вы не можете надежно разделить имена. В любом случае разные нации по-разному написали свои имена в разных порядках (например, во Франции фамилия обычно на первом месте, то же самое верно и в некоторых дальневосточных странах, но вы не можете использовать язык, чтобы обнаружить это, потому что эмигранты из этих стран часто меняли свои имена на избегайте путаницы... но не всех эмигрантов.)

И некоторые страны не имеют ожидаемой структуры имен вообще; например в России и Исландии люди по-прежнему используют покровительства, а не фамилии.

Даже на английском языке есть люди с двуствольными фамилиями, у которых нет дефисов; то есть люди с Мак, Мак, Де, де, Ван, ван и другие префиксные слова как часть их имени. Его гораздо лучше просто игнорировать проблему и задавать более разумные вопросы.

Если вы вынуждены разделять имена, например. обработки кредитных карт, я бы пошел с чем-то простым, например, раскалыванием в последнем пробеле, вместо того, чтобы пытаться быть умным и получить правильное расщепление. Его гораздо более вероятно, что карточная компания, если она занимается расщеплением, будет использовать этот наивный подход, и цель состоит в том, чтобы соответствовать их вероятному поведению. Не жалуйтесь на интерфейсы, которые разрешают только имена разделов.

Ответ 12

Я всегда предлагаю как можно больше фиксировать как можно больше независимых данных, а только требуя данных, необходимых для правильной работы функций. Использование этого метода позволяет использовать несколько сценариев форматирования и создания имен.

Независимый захват следующих полей на уровне конечного пользователя скорее всего устранит необходимость в синтаксическом анализе или, по крайней мере, устранит проблемы синтаксического разбора со специальными символами или именами разделов, такими как... "Св. Иоанн", "де ла Хойя" и "младший III".

  • приветствие (например, г-н, г-жа, д-р и т.д.).
  • данное имя (например, Джон, Мэри-Кэтрин, Мэри Лу и т.д.).
  • middlename (например, Davis, Alysia-Anne, D'Marco и т.д.).
  • фамилия (например, de la Hoya, Smith-Peters, St. John и т.д.).
  • суффикс (например, старший, младший, младший и т.д.).

После захвата эти имена могут быть перегруппированы, построены или отформатированы динамически, так как программист или конечный пользователь (опция, предоставленная программистом) считает нужным.

Ответ 13

Это моя функция, использующая регулярное выражение. Может легко распространяться на другие правила, например. более академические звания

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',
)
-----------------------------------

Ответ 14

Объявляет об этом: sans regex:

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 no middle name
            $name_array['first'] = $name_parts[0];
            $name_array['last'] = $name_parts[1].' '.$name_parts[2];
            return $name_array;            
        }
        else
        {
            // Assume it 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 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>    

И результаты:

    table {
        background-color: #ccc;
        border: 2px solid black;
    }
    td, th {
        padding: 4px 8px;
    }
    td {
        background-color: #0ff;
    }
<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>

Ответ 15

Если вы просто хотите разбить имя на:

  • Все до первого символа "пробела" как $firstName
  • Все после первого символа "пробела" как $lastName

вы можете использовать:

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

Это не самый сложный или культурно чувствительный метод, но он содержит только две строки кода и часто может выполнять работу над проектами, которые не требуют высокой степени разделения имен точности.