Як я можу сортувати масив рядків UTF-8 у PHP?

потрібна допомога у сортуванні слів за допомогою utf-8. Наприклад, у нас є 5 міст з Бельгії.

$array = array('Borgloon','Thuin','Lennik','Éghezée','Aubel');
sort($array);//Expected: Aubel, Borgloon, Éghezée, Lennik, Thuin
             //Actual: Aubel, Borgloon, Lennik, Thuin, Éghezée

Місто Éghezée має бути третім. Чи можна використовувати/встановити якийсь utf-8 або створити свій власний порядок персонажів?

15
Додано коментар, щоб зменшити плутанину щодо того, що ви шукаєте, у порівнянні з тим, що ви отримуєте.
додано Автор Billy ONeal, джерело
Я просто хотів вказати на майбутнє посилання, що natcasesort не працює з коробки: кодова панель .org/QgdF5DUY
додано Автор middus, джерело
Схоже, раніше було подібне питання: stackoverflow.com/questions/120334/ …
додано Автор user1012851, джерело

6 Відповіді

intl comes bundled with PHP from PHP 5.3 and it only supports UTF-8.

Ви можете скористатись Collator у цьому випадку:

$array = array('Borgloon','Thuin','Lennik','Éghezée','Aubel');
$collator = new Collator('en_US');
$collator->sort($array);
print_r($array);

Вихід:

Array
(
    [0] => Aubel
    [1] => Borgloon
    [2] => Éghezée
    [3] => Lennik
    [4] => Thuin
)
29
додано

Я думаю, ви можете скористатись strcoll :

setlocale(LC_COLLATE, 'nl_BE.utf8');
$array = array('Borgloon','Thuin','Lennik','Éghezée','Aubel');
usort($array, 'strcoll'); 
print_r($array);

Результат:

Array
(
    [0] => Aubel
    [1] => Borgloon
    [2] => Éghezée
    [3] => Lennik
    [4] => Thuin
)

Вам потрібен локальний файл nl_BE.utf8 у вашій системі:

[email protected]:~$ locale -a | grep nl_BE.utf8
nl_BE.utf8

Якщо ви використовуєте debian, ви можете використовувати dpkg --reconfigure locales для додавання локацій.

9
додано
strcoll не працюють на Windows за допомогою utf-8, що пов'язано з фіктивною реалізацією CRT
додано Автор Enyby, джерело
Cleaner Solution до цих пір.
додано Автор Jaison Erick, джерело
рішення для PHP 5.3 теж здається чистим
додано Автор Fy-, джерело

Цей сценарій повинен вирішитись за власним способом. Я сподіваюсь, це допоможе. Зверніть увагу на функцію mb_strtolower. Ви повинні використовувати це робить функцію регістру нечутливими. Причина, чому я не використовував функцію strtolower, полягає в тому, що вона не працює добре з спеціальними символами.

<?php

function customSort($a, $b) {
    static $charOrder = array('a', 'b', 'c', 'd', 'e', 'é',
                              'f', 'g', 'h', 'i', 'j',
                              'k', 'l', 'm', 'n', 'o',
                              'p', 'q', 'r', 's', 't',
                              'u', 'v', 'w', 'x', 'y', 'z');

    $a = mb_strtolower($a);
    $b = mb_strtolower($b);

    for($i=0;$i $valB) return 1;
        return -1;
    }

    if(mb_strlen($a) == mb_strlen($b)) return 0;
    if(mb_strlen($a) > mb_strlen($b))  return -1;
    return 1;

}
$array = array('Borgloon','Thuin','Lennik','Éghezée','Aubel');
usort($array, 'customSort');

EDIT: Вибачте. Я зробив багато помилок у останньому коді. Тепер тестовано.

EDIT {2}: все, що має багатобайтові функції.

5
додано
+1 для рішення таблиці пошуку.
додано Автор Billy ONeal, джерело
На жаль, це не спрацює, оскільки $ a [$ i] поверне один байт з рядка, а не жодного символу.
додано Автор Leonid Shevtsov, джерело
str_split також не обробляє багатобайтові рядки. :) Див. php.net/manual/en/function.mb -split.php # 99851
додано Автор Leonid Shevtsov, джерело
не запускайте strlen функцію, яка часто, вам потрібно лише запустити їх один раз, а ви вже можете отримати мінімальне значення обох.
додано Автор hakre, джерело
Раніше, так, ви були правильними. Я змінив алгоритм кілька хвилин тому. Використання str_split буде працювати.
додано Автор Jaison Erick, джерело
Ти правий. Я оновлював мультибайт скрізь.
додано Автор Jaison Erick, джерело

Що стосується strcoll я думаю, це була хороша ідея, але, здається, не працює:

<?php

// Some 
$strings = array('Alpha', 'Älpha', 'Bravo');
// make it German: A, Ä, B
setlocale(LC_COLLATE, 'de_DE.UTF8', 'de.UTF8', 'de_DE.UTF-8', 'de.UTF-8');
usort($strings, 'strcoll');
var_dump($strings);
// as you can see, Ä is last, so this didn't work

Через деякий час я написав UTF-8 до ASCII інструмент, який перетворить "älph # bla" на "aelph -Бла ". Ви можете використовувати це, щоб "нормалізувати" свій внесок, щоб зробити його сортувальним. Це в основному заміна, аналогічна тому, що сказав @Nick.

Ви маєте використовувати окремий масив для сортування, оскільки виклик urlify() у зворотному виклику usort() буде витрачати багато ресурсів. спробуй

<?php
// data to sort
$array = array('Borgloon','Thuin','Lennik','Éghezée','Aubel');
// container for modified strings
$_array = array();
foreach ($array as $k => $v) {
   //"normalize" utf8 to ascii
    $_array[$k] = urlify($v);
}
// sort the ASCII stuff (while preserving indexes)
asort($_array);
foreach ($_array as $key => &$v) {
   //copy the original value of the ASCIIfied element
    $v = $array[$k];
}
var_dump($_array);

Якщо у вас є PHP5.3 або внутрішній PECL скомпільований, спробуйте @ Thai рішення, здається солодким!

1
додано

Я маю спонукання до циклу через масив і конвертувати англійські символи перед сортуванням. Наприклад,

<?php
  $array = array('Borgloon','Thuin','Lennik','Éghezée','Aubel');

  setlocale(LC_CTYPE, 'nl_BE.utf8');

  $newarray = array();
  foreach($array as $k => $v) {
    $newarray[$k] = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $v);
  }

  sort($newarray);
  print_r($newarray);
?>

Напевно, не найкраще з точки зору обробки швидкості/ресурсів, що використовуються. Але, звичайно, полегшує розуміння коду.

Редагування:

Думаючи про це зараз, вам, можливо, буде краще використовувати якийсь таблицю пошуку, щось на зразок цього:

<?php
  $accentedCharacters = array ( 'à', 'á', 'â', 'ã', 'ä', 'å', 'ç', 'è', 'é', 'ê', 'ë', 'ì', 'í', 'î', 'ï', 'ñ', 'ò', 'ó', 'ô', 'õ', 'ö', 'ø', 'ù', 'ú', 'û', 'ü', 'ý', 'ÿ', 'Š', 'Ž', 'š', 'ž', 'Ÿ', 'À', 'Á', 'Â', 'Ã', 'Ä', 'Å', 'Ç', 'È', 'É', 'Ê', 'Ë', 'Ì', 'Í', 'Î', 'Ï', 'Ñ', 'Ò', 'Ó', 'Ô', 'Õ', 'Ö', 'Ø', 'Ù', 'Ú', 'Û', 'Ü', 'Ý' ); 

  $replacementCharacters = array ( 'a', 'a', 'a', 'a', 'a', 'a', 'c', 'e', 'e', 'e', 'e', 'i', 'i', 'i', 'i', 'n', 'o', 'o', 'o', 'o', 'o', 'o', 'u', 'u', 'u', 'u', 'y', 'y', 'S', 'Z', 's', 'z', 'Y', 'A', 'A', 'A', 'A', 'A', 'A', 'C', 'E', 'E', 'E', 'E', 'I', 'I', 'I', 'I', 'N', 'O', 'O', 'O', 'O', 'O', 'O', 'U', 'U', 'U', 'U', 'Y' );

  $array = array('Borgloon','Thuin','Lennik','Éghezée','Aubel');

  $newarray = array();
  foreach($array as $k => $v) {
    $newarray[$k] = str_replace($accentedCharacters,$replacementCharacters,$v);
  }

  sort($newarray);
  print_r($newarray);
?>
1
додано
Чесно кажучи, це був перший регіон, який прийшов на думку, що буде працювати з огляду на набір даних. Подумавши про це зараз, він може краще скористатися таблицею пошуку замість цього, якщо набір даних збирається використовувати інші ненормальні символи.
додано Автор Nick, джерело
Чому ви пропонуєте nl_BE ? (Голландська, як розмовна/написана в Бельгії)
додано Автор middus, джерело

Якщо ви хочете використовувати власне рішення, то я можу запропонувати це

function compare($a, $b)
{
        $alphabet = 'aąbcćdeęfghijklłmnnoóqprstuvwxyzźż';//i used polish letters
        $a = mb_strtolower($a);
        $b = mb_strtolower($b);

        for ($i = 0; $i < mb_strlen($a); $i++) {
            if (mb_substr($a, $i, 1) == mb_substr($b, $i, 1)) {
                continue;
            }
            if ($i > mb_strlen($b)) {
                return 1;
            }
            if (mb_strpos($alphabet, mb_substr($a, $i, 1)) > mb_strpos($alphabet, mb_substr($b, $i, 1))) {
                return 1;
            } else {
                return -1;
            }
        }
}

usort($needed_array, 'compare');

Не впевнений, що це найкраще рішення, але це працює для мене =)

1
додано
приємний, чудово.
додано Автор Eir, джерело
Малий оновлення, пов'язане з 7-м-п6 і новим оператором "космічний корабель". Ви можете використовувати <=> для повернення 1 або -1 в останньому стані.
додано Автор Amir Djaminov, джерело
Ukrainian PHP comunity
Ukrainian PHP comunity
885 учасників

dev-ua/php