Я не знаю, как отсортировать массив, содержащий строки в кодировке UTF-8 в PHP. Массив поступает с сервера LDAP, поэтому сортировка через базу данных (не проблема) не является решением. Следующее не работает на моей машине для разработки Windows (хотя я думаю, что это должно быть, по крайней мере, возможным решением):
$array=array('Birnen', 'Äpfel', 'Ungetüme', 'Apfel', 'Ungetiere', 'Österreich');
$oldLocal=setlocale(LC_COLLATE, "0");
var_dump(setlocale(LC_COLLATE, 'German_Germany.65001'));
usort($array, 'strcoll');
var_dump(setlocale(LC_COLLATE, $oldLocal));
var_dump($array);
Результат:
string(20) "German_Germany.65001"
string(1) "C"
array(6) {
[0]=>
string(6) "Birnen"
[1]=>
string(9) "Ungetiere"
[2]=>
string(6) "Äpfel"
[3]=>
string(5) "Apfel"
[4]=>
string(9) "Ungetüme"
[5]=>
string(11) "Österreich"
}
Это полная чушь. Использование 1252 в качестве кодовой страницы для setlocale() дает другой результат, но все же явно неправильный:
string(19) "German_Germany.1252"
string(1) "C"
array(6) {
[0]=>
string(11) "Österreich"
[1]=>
string(6) "Äpfel"
[2]=>
string(5) "Apfel"
[3]=>
string(6) "Birnen"
[4]=>
string(9) "Ungetüme"
[5]=>
string(9) "Ungetiere"
}
Есть ли способ отсортировать массив с учетом локали строк UTF-8?
Просто заметил, что это, похоже, проблема PHP в Windows, поскольку тот же фрагмент с de_DE.utf8, используемый в качестве локали, работает на машине Linux. Тем не менее, решение этой специфической для Windows проблемы было бы неплохо ...
Обратите внимание, что порядок сортировки зависит от языка. В немецком языке A и Ä иногда можно отсортировать, как если бы они были одной и той же буквой, а иногда Ä можно отсортировать, поскольку на самом деле это было «AE». Шведский, однако, Ä стоит в конце алфавита. Карл
Вы правы - это свойство соблюдается при использовании правильной локали и strcoll () для сортировки. Проблема здесь в том, что в Windows strcoll (), похоже, имеет проблему, когда входные строки закодированы в UTF-8.






Это очень сложный проблема, поскольку данные в кодировке UTF-8 могут содержать любой символ Unicode (т.е. символы из многих 8-битных кодировок, которые по-разному сопоставляются в разных регионах).
Возможно, если вы преобразовали данные UTF-8 в Unicode (извините, не знакомы с функциями PHP unicode), а затем нормализовали их в NFD или NFKD, а затем сортировка по кодовым точкам могла бы дать некоторую сортировку, которая будет иметь для вас смысл (например, «A» до » Ä ").
Проверьте ссылки, которые я предоставил.
Обновлено: поскольку вы упомянули, что ваши входные данные ясны (я предполагаю, что все они попадают в кодовую страницу "windows-1252"), вам следует выполнить следующее преобразование: UTF-8 → Unicode → Windows-1252, на котором Windows-1252 закодированные данные выполняют сортировку, выбирая локаль "CP1252".
Спасибо за информацию - посмотрю по ссылкам. Но я сомневаюсь, что усилия стоят результата, потому что я просто хочу отсортировать список названий стран и штатов. Возможно, есть более простое решение.
Вроде разумное решение ... Попробую отсортировать преобразованный массив. Вы правы, что Windows-1252 должна охватывать все используемые символы.
Что значит преобразовать UTF-8 в Unicode. UTF-8 - это кодировка символов переменной длины для Unicode.
Я имею в виду строку байтов кодовых точек Unicode, закодированных как UTF-8, для внутреннего представления в виде строки кодовых точек Unicode, независимо от того, какое представление было бы в PHP (будь то UCS-2, UCS-4). Я предполагаю, что в PHP есть такая концепция.
В PHP нет такого понятия :(
Использование вашего примера с кодовой страницей 1252 отлично работало здесь, на моей машине для разработки Windows.
$array=array('Birnen', 'Äpfel', 'Ungetüme', 'Apfel', 'Ungetiere', 'Österreich');
$oldLocal=setlocale(LC_COLLATE, "0");
var_dump(setlocale(LC_COLLATE, 'German_Germany.1252'));
usort($array, 'strcoll');
var_dump(setlocale(LC_COLLATE, $oldLocal));
var_dump($array);
... отрезать ...
Это было с PHP 5.2.6. Кстати.
function traceStrColl($a, $b) {
$outValue = strcoll($a, $b);
echo "$a $b $outValue\r\n";
return $outValue;
}
$array=array('Birnen', 'Äpfel', 'Ungetüme', 'Apfel', 'Ungetiere', 'Österreich');
setlocale(LC_COLLATE, 'German_Germany.65001');
usort($array, 'traceStrColl');
print_r($array);
дает:
Ungetüme Äpfel 2147483647 Ungetüme Birnen 2147483647 Ungetüme Apfel 2147483647 Ungetüme Ungetiere 2147483647 Österreich Ungetüme 2147483647 Äpfel Ungetiere 2147483647 Äpfel Birnen 2147483647 Apfel Äpfel 2147483647 Ungetiere Birnen 2147483647
Я нашел несколько отчеты об ошибках, которые были помечены как фальшивка ... Лучше всего, что у вас есть, - это заполнить отчет об ошибке, я полагаю ...
Вы уверены, что ваш PHP-файл, используемый для тестирования, имеет кодировку UTF-8? Если я использую кодировку ISO-8859-1 для самого файла, я получу тот же результат, который вы опубликовали выше.
Я дважды проверил его вторым файлом (убедился, что он закодирован в UTF-8), но теперь он (действительно), кажется, воспроизводит вашу проблему, извините за дерьмо в этом случае.
Ваша сортировка должна соответствовать набору символов. Поскольку ваши данные закодированы в UTF-8, вам следует использовать параметры сортировки UTF-8. На разных платформах он может называться по-разному, но лучше предположить, что это будет de_DE.utf8.
В системах UNIX вы можете получить список установленных в настоящее время локалей с помощью команды
locale -a
Я использую Windows-машину для разработки ... Подходящей кодовой страницей UTF-8 в Windows является 65001 - вот почему моя локаль должна быть German_Germany.65001.
В конце концов, эта проблема не может быть решена простым способом без использования перекодированных строк (UTF-8 → Windows-1252 или ISO-8859-1), как предлагает ΤΖΩΤΖΙΟΥ, из-за очевидной ошибки PHP, обнаруженной Хуппи. Чтобы обобщить проблему, я создал следующий фрагмент кода, который ясно демонстрирует, что проблема заключается в функции strcoll () при использовании кодовой страницы 65001 Windows-UTF-8.
function traceStrColl($a, $b) {
$outValue=strcoll($a, $b);
echo "$a $b $outValue\r\n";
return $outValue;
}
$locale=(defined('PHP_OS') && stristr(PHP_OS, 'win')) ? 'German_Germany.65001' : 'de_DE.utf8';
$string = "ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÜabcdefghijklmnopqrstuvwxyzäöüß";
$array=array();
for ($i=0; $i<mb_strlen($string, 'UTF-8'); $i++) {
$array[]=mb_substr($string, $i, 1, 'UTF-8');
}
$oldLocale=setlocale(LC_COLLATE, "0");
var_dump(setlocale(LC_COLLATE, $locale));
usort($array, 'traceStrColl');
setlocale(LC_COLLATE, $oldLocale);
var_dump($array);
Результат:
string(20) "German_Germany.65001"
a B 2147483647
[...]
array(59) {
[0]=>
string(1) "c"
[1]=>
string(1) "B"
[2]=>
string(1) "s"
[3]=>
string(1) "C"
[4]=>
string(1) "k"
[5]=>
string(1) "D"
[6]=>
string(2) "ä"
[7]=>
string(1) "E"
[8]=>
string(1) "g"
[...]
Тот же фрагмент кода работает на компьютере с Linux без каких-либо проблем, создавая следующий результат:
string(10) "de_DE.utf8"
a B -1
[...]
array(59) {
[0]=>
string(1) "a"
[1]=>
string(1) "A"
[2]=>
string(2) "ä"
[3]=>
string(2) "Ä"
[4]=>
string(1) "b"
[5]=>
string(1) "B"
[6]=>
string(1) "c"
[7]=>
string(1) "C"
[...]
Этот фрагмент также работает при использовании строк в кодировке Windows-1252 (ISO-8859-1) (конечно, тогда необходимо изменить кодировку mb_ * и языковой стандарт).
Я отправил отчет об ошибке на bugs.php.net: Ошибка № 46165 strcoll () не работает со строками UTF-8 в Windows.. Если у вас возникла та же проблема, вы можете оставить свой отзыв команде PHP на странице отчета об ошибке (две другие, вероятно, связанные ошибки были классифицированы как фальшивка - я не думаю, что это ошибка фальшивка ;-).
Спасибо вам всем.
Обновление по этому вопросу:
Несмотря на то, что обсуждение этой проблемы показало, что мы могли обнаружить ошибку PHP с strcoll() и / или setlocale(), это явно не так. Проблема скорее заключается в ограничении реализации setlocale() в Windows CRT (PHP setlocale() - это просто тонкая оболочка вокруг вызова CRT). Ниже приводится ссылка на Страница MSDN "setlocale, _wsetlocale":
The set of available languages, country/region codes, and code pages includes all those supported by the Win32 NLS API except code pages that require more than two bytes per character, such as UTF-7 and UTF-8. If you provide a code page like UTF-7 or UTF-8, setlocale will fail, returning NULL. The set of language and country/region codes supported by setlocale is listed in Language and Country/Region Strings.
Поэтому невозможно использовать строковые операции с учетом локали в PHP в Windows, когда строки закодированы в многобайтовом формате.
Это является ошибка в PHP, полагающаяся на способность ОС правильно сопоставлять строки, зная, что некоторые ОС этого не делают. Если тонкой оболочки недостаточно, PHP следует использовать что-то еще.
$a = array( 'Кръстев', 'Делян1', 'делян1', 'Делян2', 'делян3', 'кръстев' );
$col = new \Collator('bg_BG');
$col->asort( $a );
var_dump( $a );
Печать:
array
2 => string 'делян1' (length=11)
1 => string 'Делян1' (length=11)
3 => string 'Делян2' (length=11)
4 => string 'делян3' (length=11)
5 => string 'кръстев' (length=14)
0 => string 'Кръстев' (length=14)
Класс Collator определен в Расширение PECL intl. Он распространяется с исходными кодами PHP 5.3, но может быть отключен для некоторых сборок. Например. в Debian он находится в пакете php5-intl.
Collator::compare полезен для usort.
Расширение ext/intl действительно спасло меня - к сожалению, его не так просто установить в некоторых системах (например, Mac OS X со встроенным PHP).
Я часто разрабатываю на Mac и никогда не использую в комплекте, например. Стек ЛАМПА. Вместо этого установите что-то вроде homebrew или macports, и вы избавите себя от некоторых проблем (и, возможно, добавите еще немного, но это будет меньшее количество проблем)
Мне очень помогает нашел эту следующую вспомогательную функцию для преобразования всех букв строки в буквы ASCII.
function _all_letters_to_ASCII($string) {
return strtr(utf8_decode($string),
utf8_decode('ŠŒŽšœžŸ¥µÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýÿ'),
'SOZsozYYuAAAAAAACEEEEIIIIDNOOOOOOUUUUYsaaaaaaaceeeeiiiionoooooouuuuyy');
}
После этого простой array_multisort() даст вам то, что вы хотите.
$array = array('Birnen', 'Äpfel', 'Ungetüme', 'Apfel', 'Ungetiere', 'Österreich');
$reference_array = $array;
foreach ($reference_array as $key => &$value) {
$value = _all_letters_to_ASCII($value);
}
var_dump($reference_array);
array_multisort($reference_array, $array);
var_dump($array);
Конечно, вы можете приспособить вспомогательную функцию к более продвинутым потребностям. Но пока все выглядит неплохо.
array(6) {
[0]=> string(6) "Birnen"
[1]=> string(5) "Apfel"
[2]=> string(8) "Ungetume"
[3]=> string(5) "Apfel"
[4]=> string(9) "Ungetiere"
[5]=> string(10) "Osterreich"
}
array(6) {
[0]=> string(5) "Apfel"
[1]=> string(6) "Äpfel"
[2]=> string(6) "Birnen"
[3]=> string(11) "Österreich"
[4]=> string(9) "Ungetiere"
[5]=> string(9) "Ungetüme"
}
У меня такая же проблема с немецким «Умлауте». После некоторых исследований это сработало для меня:
$laender =array("Österreich", "Schweiz", "England", "France", "Ägypten");
$laender = array_map("utf8_decode", $laender);
setlocale(LC_ALL,"de_DE@euro", "de_DE", "deu_deu");
sort($laender, SORT_LOCALE_STRING);
$laender = array_map("utf8_encode", $laender);
print_r($laender);
Результат:
Array
(
[0] => Ägypten
[1] => England
[2] => France
[3] => Österreich
[4] => Schweiz
)
Проблема здесь в том, что вы потеряете символы, которые не могут быть представлены в ISO-8859-1.
Здесь он работал отлично (см. Мой пост ниже), вы уверены, что это не имеет ничего общего с конфигурацией машины?