Подскажите оптимальное решение такой задачи: Из строки вида 'Фамилия И.О., И.О. Фамилия, 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
Можно и проще. PHP: <pre> <?php $authors = "Шишкин И.И.,И Андрей, ,Сан-Антонио,Петров-Водкин К. С. ,Вл.А.Сидоров-Ивановский, M.Yu. Ivanov, D. Vrel, Dash Jh. B."; $array = explode(',', $authors); $names = array(); foreach($array as $name) { $values = preg_split("/[\s.]+/", trim($name, '. ')); $count = count($values); if(!isset($values[1])) { if(!empty($values[0])) $names[] = $values[0]; } elseif(strlen($values[0]) > strlen($values[$count-1])) { $lastname = array_shift($values); $names[] = sprintf('%s %s.', $lastname, join('. ', $values)); } else { $lastname = array_pop($values); $names[] = sprintf('%s %s.', $lastname, join('. ', $values)); } } print_r($names); ?> Но корректно работать будет, если фамилия длиннее имени или первого инициала. Для примера проблемного имени добавил Андрея И
Небольшое уточнение условий задачи: Инициал(ы) — _всегда_ заканчиваются точками и присутствуют всегда. Их может быть либо 1 либо 2. И они отделяются от фамилии либо минимум 1 пробелом (если идут после фамилии — 'Фамилия И.О.') либо могут не отделяться (если идут до фамилии — 'И.О.Фамилия'). Т.е. в исходном списке (который через запятую) не может быть элементов вроде 'И Андрей' (инициал не завершается точкой), 'Сан-Антонио' (инициалов нет) или пустых/пробельных — ' '. Примечание: Смысл задачи в том, что при публикации данных о различных книгах и статьях инициалы авторов могут указываться в разных форматах: * с инициалами в начале (И.О. Фамилия) или в конце (Фамилия И.О.) * с пробелами между инициалами (Фамилия И. О.) или без (И.О.Фамилия) И нужно привести все ФИО авторов к единому формату, чтобы было удобно сортировать авторов (по алфавиту), для последующей генерации авторского указателя.
У автора может не быть инициалов. Псевдоним например. Как Сан-Антонио. У автора помимо инициалов могут быть другие атрибуты. Например 'ибн", "фон", "акад.", "док.", "md" и прочее. Поэтому рекомендуется в код вставить дополнительное условие. Если разобранная в массив $name состоит не из трех элементов, то оставлять её как есть, но помещать в дополнительный массив с некондицией. Для последующей правки вручную например.
Благодарю за замечания, но * На данный момент (несколько сотен научных книжек и статей в базе) псевдонимов без инициалов не встречалось и крайне маловероятно, что будут * Приставки вроде ибн, фон, ван и т.п. возможны. И они, очевидно, входят в результирующий список как часть фамилии. Вроде такого: 'van der Waals J.D.' * Ученые звания и степени вроде "акад.", "док." и т.п. обычно не используются в списках авторов публикаций. По крайней мере, ничего похожего не встречалось. Поэтому усложнять структуру вывода пока нет необходимости. Хотелось бы решить эту задачу в рамках существующих условий. А усложнять только по мере возникновения необходимости. ))
Так она в скрипте выше решена. Имена типа "Фамилия И.О." и "И.О. Фамилия" с любым количеством пробельных сиволов и точек разбиваются в массив, если первый его элемент длиннее последнего, то он считается фамилией, если нет, то наоборот. Оставшиеся элементы объединяются в строку через разделитель '. ', плюс еще одна точка в конец.
Я бы так сделал: - искал бы регуляркой инициалы - а потом их вырезал, это стала бы фамилия PHP: $authors = "Ив. Шишкин , ибн Иванов А.Б., Петров-Водкин К. С. ,В.А.Сидоров-Ивановский, M.Yu. Ivanov, D. Vrel, Dash Jh. B."; $arr = explode(',', $authors); $res = []; foreach ($arr as $val) { if ($fio = process($val)) { $res[] = $fio; } } var_dump($res); function process($str) { $match = []; if (preg_match('/(\w+\.\s*(\w+\.)?)/iu', $str, $match)) { if (isset($match[1])) { return trim(str_replace($match[1], '', $str)) . ' ' . str_replace(' ', '', $match[1]); } } return false; }