За последние 24 часа нас посетили 20756 программистов и 1010 роботов. Сейчас ищут 394 программиста ...

Обработка строки с фамилиями и инициалами

Тема в разделе "PHP для новичков", создана пользователем rr33rr, 6 май 2021.

  1. rr33rr

    rr33rr Новичок

    С нами с:
    6 май 2021
    Сообщения:
    33
    Симпатии:
    18
    Подскажите оптимальное решение такой задачи:

    Из строки вида 'Фамилия И.О., И.О. Фамилия, Surname Fr. P.'
    Т.е. из списка фамилий с инициалами (допустимы русские и англ. символы), через запятую.
    В исходной строке инициалы могут быть до и после фамилии
    Инициалы могут быть отделены от фамилий и друг от друга неизвестным числом пробелов (0 или более)
    Инициалов может быть 1 или 2
    Длина инициалов неизвестна (т.е. они могут состоять из 1 и более символов)
    Нужно получить массив из строк вида 'Фамилия И.О.' или 'Фамилия И.' (если инициал только один)
    Т.е. первой идет фамилия, а за ней через 1 пробел инициал(ы)
    После чего отсортировать результат по алфавиту
    Т.е. из примерно такой исходной строки:
    $authors = "Шишкин И.И.,Иванов А.Б., Петров-Водкин К. С. ,В.А.Сидоров-Ивановский, M.Yu. Ivanov, D. Vrel, Dash Jh. B.";
    должно получиться так:
    'Dash Jh.B.'
    'Ivanov M.Yu.'
    'Vrel D.'
    'Иванов А.Б.'
    'Петров-Водкин К.С.'
    'Сидоров-Ивановский В.А.'
    'Шишкин И.И.'

    Ниже попытка ее решить. Вроде нужный результат достигнут. НО т.к. я - новичок в PHP и всяких регулярках, 100% код не оптимальный и наверняка можно лучше и проще. ))
    Код (Text):
    1. <?php
    2. //Функция для сортировки
    3. function cmp($a, $b) {
    4.     return strcmp($a, $b);
    5. }
    6. //Исходная строка
    7. $authors = "Шишкин И.И.,Иванов А.Б.,  Петров-Водкин К.  С. ,В.А.Сидоров-Ивановский, M.Yu. Ivanov, D.     Vrel, Dash Jh. B.";
    8.  
    9. $author = explode(',', $authors);
    10. echo "<b>Input:</b>
    11. <pre>\n";
    12. echo "'", $authors, "'\n";
    13. echo "</pre>\n";
    14. $total = count($author);
    15. for ($i = 0; $i < $total; $i++) {
    16.     //убираем ведущие и завершающие пробелы
    17.     $author[$i] = trim($author[$i]);
    18.     //вырезаем подстроку до последнего вхождения точки
    19.     $tmp = preg_replace('/[^.]+$/u', '', $author[$i]);
    20.     //если полученная подстрока не совпадает с исходной, т.е. инициалы шли до фамилии
    21.     if ($author[$i] != $tmp){
    22.         //то удаляем инициалы из исходной строки
    23.         $author[$i] = trim(str_replace($tmp, '', $author[$i]));
    24.         //убираем все пробелы из инициалов
    25.         $tmp = preg_replace('/\s+/','',$tmp);
    26.         //дописываем инициалы через пробел после фамилии
    27.         $author[$i] = $author[$i] . " " . $tmp;
    28.     }
    29.     //если инициалы шли после фамилии
    30.     else {
    31.         //вырезаем подстроку с первого пробела
    32.         $tmp = substr($author[$i], strpos($author[$i],' ')+1, strlen($author[$i]));
    33.         // до последней точки включительно
    34.         $tmp = substr($tmp, 0, strrpos($tmp,'.')+1);
    35.         //удаляем инициалы из ФИО и убираем ведущие и завершающие пробелы
    36.         $author[$i] = trim(str_replace($tmp, '', $author[$i]));
    37.         //убираем пробелы из инициалов
    38.         $tmp = preg_replace('/\s+/','',$tmp);
    39.         //дописываем полученные инициалы через пробел после фамилии
    40.         $author[$i] = $author[$i] . " " . $tmp;
    41.     }
    42. }
    43.  
    44. //Сортируем результат
    45. usort($author, 'cmp');
    46. echo "<b>Output:</b>\n<pre>\n";
    47. foreach ($author as $a) {
    48.     echo "'", $a, "'\n";
    49. }
    50. echo "</pre>\n";
    51. ?>
    Оно же в php-песочнице: https://sandbox.onlinephpfunctions.com/code/304950b353c471999ee355359ddc3da6c0d1b438
     
  2. Drunkenmunky

    Drunkenmunky Активный пользователь

    С нами с:
    12 авг 2020
    Сообщения:
    1.476
    Симпатии:
    281
    Можно и проще.
    PHP:
    1. <pre>
    2. <?php
    3. $authors = "Шишкин И.И.,И Андрей,   ,Сан-Антонио,Петров-Водкин К. С. ,Вл.А.Сидоров-Ивановский, M.Yu. Ivanov, D. Vrel, Dash Jh. B.";
    4.  
    5. $array = explode(',', $authors);
    6.  
    7. $names = array();
    8.  
    9. foreach($array as $name)
    10. {
    11. $values = preg_split("/[\s.]+/", trim($name, '. '));
    12. $count = count($values);
    13.  
    14. if(!isset($values[1]))
    15. {
    16.    if(!empty($values[0]))
    17.    $names[] = $values[0];
    18. }
    19. elseif(strlen($values[0]) > strlen($values[$count-1]))
    20. {
    21.   $lastname = array_shift($values);
    22.   $names[] = sprintf('%s %s.', $lastname, join('. ', $values));
    23. }
    24. else
    25. {
    26.   $lastname = array_pop($values);
    27.   $names[] = sprintf('%s %s.', $lastname, join('. ', $values));
    28. }
    29. }
    30.  
    31. print_r($names);
    32. ?>
    Но корректно работать будет, если фамилия длиннее имени или первого инициала.
    Для примера проблемного имени добавил Андрея И
     
    rr33rr нравится это.
  3. rr33rr

    rr33rr Новичок

    С нами с:
    6 май 2021
    Сообщения:
    33
    Симпатии:
    18
    Небольшое уточнение условий задачи:

    Инициал(ы) — _всегда_ заканчиваются точками и присутствуют всегда. Их может быть либо 1 либо 2. И они отделяются от фамилии либо минимум 1 пробелом (если идут после фамилии — 'Фамилия И.О.') либо могут не отделяться (если идут до фамилии — 'И.О.Фамилия').

    Т.е. в исходном списке (который через запятую) не может быть элементов вроде 'И Андрей' (инициал не завершается точкой), 'Сан-Антонио' (инициалов нет) или пустых/пробельных — ' '.

    Примечание:
    Смысл задачи в том, что при публикации данных о различных книгах и статьях инициалы авторов могут указываться в разных форматах:
    * с инициалами в начале (И.О. Фамилия) или в конце (Фамилия И.О.)
    * с пробелами между инициалами (Фамилия И. О.) или без (И.О.Фамилия)

    И нужно привести все ФИО авторов к единому формату, чтобы было удобно сортировать авторов (по алфавиту), для последующей генерации авторского указателя.
     
  4. Drunkenmunky

    Drunkenmunky Активный пользователь

    С нами с:
    12 авг 2020
    Сообщения:
    1.476
    Симпатии:
    281
    У автора может не быть инициалов. Псевдоним например. Как Сан-Антонио.
    У автора помимо инициалов могут быть другие атрибуты. Например 'ибн", "фон", "акад.", "док.", "md" и прочее.
    Поэтому рекомендуется в код вставить дополнительное условие.
    Если разобранная в массив $name состоит не из трех элементов, то оставлять её как есть, но помещать в дополнительный массив с некондицией. Для последующей правки вручную например.
     
    rr33rr нравится это.
  5. rr33rr

    rr33rr Новичок

    С нами с:
    6 май 2021
    Сообщения:
    33
    Симпатии:
    18
    Благодарю за замечания, но
    * На данный момент (несколько сотен научных книжек и статей в базе) псевдонимов без инициалов не встречалось и крайне маловероятно, что будут
    * Приставки вроде ибн, фон, ван и т.п. возможны. И они, очевидно, входят в результирующий список как часть фамилии. Вроде такого: 'van der Waals J.D.'
    * Ученые звания и степени вроде "акад.", "док." и т.п. обычно не используются в списках авторов публикаций. По крайней мере, ничего похожего не встречалось.

    Поэтому усложнять структуру вывода пока нет необходимости. Хотелось бы решить эту задачу в рамках существующих условий. А усложнять только по мере возникновения необходимости. ))
     
  6. Drunkenmunky

    Drunkenmunky Активный пользователь

    С нами с:
    12 авг 2020
    Сообщения:
    1.476
    Симпатии:
    281
    Так она в скрипте выше решена.
    Имена типа "Фамилия И.О." и "И.О. Фамилия" с любым количеством пробельных сиволов и точек разбиваются в массив, если первый его элемент длиннее последнего, то он считается фамилией, если нет, то наоборот. Оставшиеся элементы объединяются в строку через разделитель '. ', плюс еще одна точка в конец.
     
    rr33rr нравится это.
  7. Дюран

    Дюран Активный пользователь

    С нами с:
    9 мар 2018
    Сообщения:
    256
    Симпатии:
    19
    Я бы так сделал:
    - искал бы регуляркой инициалы
    - а потом их вырезал, это стала бы фамилия
    PHP:
    1. $authors = "Ив. Шишкин , ибн Иванов А.Б., Петров-Водкин К. С. ,В.А.Сидоров-Ивановский, M.Yu. Ivanov, D. Vrel, Dash Jh. B.";
    2. $arr = explode(',', $authors);
    3. $res = [];
    4. foreach ($arr as $val) {
    5.     if ($fio = process($val)) {
    6.         $res[] = $fio;
    7.     }
    8. }
    9. var_dump($res);
    10.  
    11. function process($str)
    12. {
    13.     $match = [];
    14.     if (preg_match('/(\w+\.\s*(\w+\.)?)/iu', $str, $match)) {
    15.         if (isset($match[1])) {
    16.             return trim(str_replace($match[1], '', $str)) . ' ' . str_replace(' ', '', $match[1]);
    17.         }
    18.     }
    19.     return false;
    20. }
     
    rr33rr нравится это.
  8. rr33rr

    rr33rr Новичок

    С нами с:
    6 май 2021
    Сообщения:
    33
    Симпатии:
    18
    Спасибо.
    Примерно в эту сторону я и думал, но незнание регулярок мешало грамотно реализовать. ))