rr33rr
Подскажите оптимальное решение такой задачи:
Из строки вида ‘Фамилия И.О., И.О. Фамилия, 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):
<?php //Функция для сортировки function cmp($a, $b) { return strcmp($a, $b); } //Исходная строка $authors = «Шишкин И.И.,Иванов А.Б., Петров-Водкин К. С. ,В.А.Сидоров-Ивановский, M.Yu. Ivanov, D. Vrel, Dash Jh. B.»; $author = explode(‘,’, $authors); echo «<b>Input:</b> <pre>n»; echo «‘», $authors, «‘n»; echo «</pre>n»; $total = count($author); for ($i = 0; $i < $total; $i++) { //убираем ведущие и завершающие пробелы $author[$i] = trim($author[$i]); //вырезаем подстроку до последнего вхождения точки $tmp = preg_replace(‘/[^.]+$/u’, », $author[$i]); //если полученная подстрока не совпадает с исходной, т.е. инициалы шли до фамилии if ($author[$i] != $tmp){ //то удаляем инициалы из исходной строки $author[$i] = trim(str_replace($tmp, », $author[$i])); //убираем все пробелы из инициалов $tmp = preg_replace(‘/s+/’,»,$tmp); //дописываем инициалы через пробел после фамилии $author[$i] = $author[$i] . » » . $tmp; } //если инициалы шли после фамилии else { //вырезаем подстроку с первого пробела $tmp = substr($author[$i], strpos($author[$i],’ ‘)+1, strlen($author[$i])); // до последней точки включительно $tmp = substr($tmp, 0, strrpos($tmp,’.’)+1); //удаляем инициалы из ФИО и убираем ведущие и завершающие пробелы $author[$i] = trim(str_replace($tmp, », $author[$i])); //убираем пробелы из инициалов $tmp = preg_replace(‘/s+/’,»,$tmp); //дописываем полученные инициалы через пробел после фамилии $author[$i] = $author[$i] . » » . $tmp; } } //Сортируем результат usort($author, ‘cmp’); echo «<b>Output:</b>n<pre>n»; foreach ($author as $a) { echo «‘», $a, «‘n»; } echo «</pre>n»; ?>Оно же в php-песочнице: https://sandbox.onlinephpfunctions.com/code/304950b353c471999ee355359ddc3da6c0d1b438
Drunkenmunky
Можно и проще.
PHP:
<pre> <?php $authors = «Шишкин И.И.,И Андрей, ,Сан-Антонио,Петров-Водкин К. С. ,Вл.А.Сидоров-Ивановский, M.Yu. Ivanov, D. Vrel, Dash Jh. B.»; foreach($array as $name) { { $names[] = $values[0]; } { } else { } } ?>Но корректно работать будет, если фамилия длиннее имени или первого инициала.
Для примера проблемного имени добавил Андрея И
rr33rr
Небольшое уточнение условий задачи:
Инициал(ы) — _всегда_ заканчиваются точками и присутствуют всегда. Их может быть либо 1 либо 2. И они отделяются от фамилии либо минимум 1 пробелом (если идут после фамилии — ‘Фамилия И.О.’) либо могут не отделяться (если идут до фамилии — ‘И.О.Фамилия’).
Т.е. в исходном списке (который через запятую) не может быть элементов вроде ‘И Андрей’ (инициал не завершается точкой), ‘Сан-Антонио’ (инициалов нет) или пустых/пробельных — ‘ ‘.
Примечание:
Смысл задачи в том, что при публикации данных о различных книгах и статьях инициалы авторов могут указываться в разных форматах:
* с инициалами в начале (И.О. Фамилия) или в конце (Фамилия И.О.)
* с пробелами между инициалами (Фамилия И. О.) или без (И.О.Фамилия)И нужно привести все ФИО авторов к единому формату, чтобы было удобно сортировать авторов (по алфавиту), для последующей генерации авторского указателя.
Drunkenmunky
У автора может не быть инициалов. Псевдоним например. Как Сан-Антонио.
У автора помимо инициалов могут быть другие атрибуты. Например ‘ибн», «фон», «акад.», «док.», «md» и прочее.
Поэтому рекомендуется в код вставить дополнительное условие.
Если разобранная в массив $name состоит не из трех элементов, то оставлять её как есть, но помещать в дополнительный массив с некондицией. Для последующей правки вручную например.
rr33rr
Благодарю за замечания, но
* На данный момент (несколько сотен научных книжек и статей в базе) псевдонимов без инициалов не встречалось и крайне маловероятно, что будут
* Приставки вроде ибн, фон, ван и т.п. возможны. И они, очевидно, входят в результирующий список как часть фамилии. Вроде такого: ‘van der Waals J.D.’
* Ученые звания и степени вроде «акад.», «док.» и т.п. обычно не используются в списках авторов публикаций. По крайней мере, ничего похожего не встречалось.Поэтому усложнять структуру вывода пока нет необходимости. Хотелось бы решить эту задачу в рамках существующих условий. А усложнять только по мере возникновения необходимости. ))
Drunkenmunky
Так она в скрипте выше решена.
Имена типа «Фамилия И.О.» и «И.О. Фамилия» с любым количеством пробельных сиволов и точек разбиваются в массив, если первый его элемент длиннее последнего, то он считается фамилией, если нет, то наоборот. Оставшиеся элементы объединяются в строку через разделитель ‘. ‘, плюс еще одна точка в конец.
Дюран
Я бы так сделал:
— искал бы регуляркой инициалы
— а потом их вырезал, это стала бы фамилияPHP:
$authors = «Ив. Шишкин , ибн Иванов А.Б., Петров-Водкин К. С. ,В.А.Сидоров-Ивановский, M.Yu. Ivanov, D. Vrel, Dash Jh. B.»; $res = []; foreach ($arr as $val) { if ($fio = process($val)) { $res[] = $fio; } } function process($str) { $match = []; } } return false; }
rr33rr
Спасибо.
Примерно в эту сторону я и думал, но незнание регулярок мешало грамотно реализовать. ))
