Алгоритм естественной сортировки в PHP с поддержкой Unicode?

Можно ли отсортировать массив с символами Unicode / UTF-8 в PHP с использованием алгоритма естественного порядка? Например (порядок в этом массиве правильно упорядочен):

$array = array ( 0 => 'Agile', 1 => 'Ágile', 2 => 'Àgile', 3 => 'Âgile', 4 => 'Ägile', 5 => 'Ãgile', 6 => 'Test', ); 

Если я попытаюсь с asort ($ array), я получаю следующий результат:

 Array ( [0] => Agile [6] => Test [2] => Àgile [1] => Ágile [3] => Âgile [5] => Ãgile [4] => Ägile ) 

И используя natsort ($ array):

 Array ( [2] => Àgile [1] => Ágile [3] => Âgile [5] => Ãgile [4] => Ägile [0] => Agile [6] => Test ) 

Как я могу реализовать функцию, которая возвращает правильный порядок результата (0, 1, 2, 3, 4, 5, 6) в PHP 5? Все многобайтовые строковые функции (mbstring, iconv, …) доступны в моей системе.

EDIT: я хочу, чтобы natsort () значения, а не ключи, – единственная причина, по которой я явно определяю ключи (а вместо as sort () используется asort ()), чтобы облегчить работу по выяснению, где сортировка Значения unicode пошли не так.

Вопрос не так легко ответить, как кажется на первый взгляд. Это одна из областей, в которой поддержка PHP без поддержки Unicode поражает вас в полной мере.

Фрист всех natsort() предложенный другими плакатами, не имеет ничего общего с сортировкой массивов типа, который вы хотите отсортировать. То, что вы ищете, – это механизм сортировки, известный в локали, поскольку сортировка строк с расширенными символами всегда связана с используемым языком. Возьмем, например, немецкий: A и Ä иногда могут быть отсортированы так, как если бы они были одной и той же буквой (DIN 5007/1), а иногда Ä можно сортировать так, как это было на самом деле «AE» (DIN 5007/2). На шведском языке, напротив, Ä приходит в конце алфавита.

Если вы не используете Windows, вам повезло, поскольку PHP предоставляет некоторые функции именно этому. Используя комбинацию setlocale() , usort() , strcoll() и правильной локали UTF-8 для вашего языка, вы получаете что-то вроде этого:

 $array = array('Àgile', 'Ágile', 'Âgile', 'Ãgile', 'Ägile', 'Agile', 'Test'); $oldLocal = setlocale(LC_COLLATE, '<<your_RFC1766_language_code>>.utf8'); usort($array, 'strcoll'); setlocale(LC_COLLATE, $oldLocal); 

Обратите внимание, что для сортировки строк UTF-8 обязательно использовать вариант локализации UTF-8. Я возвращаю языковой стандарт в приведенном выше примере до его первоначального значения, так как установка локали с помощью setlocale() может приводить к побочным эффектам в другом запущенном скрипте PHP – более подробную информацию см. В руководстве по PHP.

Когда вы используете Windows-машину, в настоящее время нет решения этой проблемы, и до того, как я начну использовать PHP 6, я не буду этого делать. Пожалуйста, см. Мой собственный вопрос о SO, предназначенный для этой конкретной проблемы.

Успешно справился!

 $array = array('Ägile', 'Ãgile', 'Test', 'カタカナ', 'かたかな', 'Ágile', 'Àgile', 'Âgile', 'Agile'); function Sortify($string) { return preg_replace('~&([az]{1,2})(acute|cedil|circ|grave|lig|orn|ring|slash|tilde|uml);~i', '$1' . chr(255) . '$2', htmlentities($string, ENT_QUOTES, 'UTF-8')); } array_multisort(array_map('Sortify', $array), $array); 

Вывод:

 Array ( [0] => Agile [1] => Ágile [2] => Âgile [3] => Àgile [4] => Ãgile [5] => Ägile [6] => Test [7] => かたかな[8] => カタカナ) 

Даже лучше:

 if (extension_loaded('intl') === true) { collator_asort(collator_create('root'), $array); } 

Благодаря @tchrist!

 natsort($array); $array = array_values($array); 

Я боролся с этим вопросом.

Сортировка:

 Array ( [xa] => África [xo] => Australasia [cn] => China [gb] => Reino Unido [us] => Estados Unidos [ae] => Emiratos Árabes Unidos [jp] => Japón [lk] => Sri Lanka [xe] => Europa Del Este [xw] => Europa Del Oeste [fr] => Francia [de] => Alemania [be] => Bélgica [nl] => Holanda [es] => España ) 

Положите África в конец. Я решил это с помощью этого грязного маленького фрагмента кода (который подходит для моей цели и моих временных рамок):

 $sort = array(); foreach($retval AS $key => $value) { $v = str_replace('ä', 'a', $value); $v = str_replace('Ä', 'A', $v); $v = str_replace('Á', 'A', $v); $v = str_replace('é', 'e', $v); $v = str_replace('ö', 'o', $v); $v = str_replace('ó', 'o', $v); $v = str_replace('Ö', 'O', $v); $v = str_replace('ü', 'u', $v); $v = str_replace('Ü', 'U', $v); $v = str_replace('ß', 'S', $v); $v = str_replace('ñ', 'n', $v); $sort[] = "$v|$key|$value"; } sort($sort); $retval = array(); foreach($sort AS $value) { $arr = explode('|', $value); $retval[$arr[1]] = $arr[2]; } 

У меня есть еще одно обходное решение для этих setlocale не работает и не имеет intl модуля:

 // The array to be sorted $countries = array( 'AT' => Österreich, 'DE' => Deutschland, 'CH' => Schweiz, ); // Extend this array to your needs. $utf_sort_map = array( "ä" => "a", "Ä" => "A", "Å" => "A", "ö" => "o", "Ö" => "O", "ü" => "u", "Ü" => "U", ); uasort($my_array, function($a, $b) use ($utf_sort_map) { $initial_a = mb_substr($a, 0, 1); $initial_b = mb_substr($b, 0, 1); if (isset($utf_sort_map[$initial_a]) || isset($utf_sort_map[$initial_b])) { if (isset($utf_sort_map[$initial_a])) { $initial_a = $utf_sort_map[$initial_a]; } if (isset($utf_sort_map[$initial_b])) { $initial_b = $utf_sort_map[$initial_b]; } if ($initial_a == $initial_b) { return mb_substr($a, 1) < mb_substr($b, 1) ? -1 : 1; } else { return $initial_a < $initial_b ? -1 : 1; } } return $a < $b ? -1 : 1; });