PHP'de bir dizi UTF-8 dizesini nasıl sıralayabilirim?

utf-8 ile kelimeleri sıralamak için yardıma ihtiyacım var. Örneğin, Belçika'dan 5 şehirimiz var.

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

Şehir Éghezée üçüncü olmalıdır. Bir çeşit utf-8'i kullanmak/ayarlamak veya kendi karakter sıramımı oluşturmak mümkün mü?

15
Ne aldığınıza karşı ne aradığınıza dair karışıklığı azaltmak için bir yorum ekledik.
katma yazar Billy ONeal, kaynak
Sadece natcasesort 'un kutudan çıkmadığını ileri sürmek için işaret etmek istedim: codepad .org/QgdF5DUY
katma yazar middus, kaynak
Daha önce benzer bir soruya benziyor: stackoverflow.com/questions/120334/…
katma yazar user1012851, kaynak

6 cevap

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

Bu durumda bir Collator kullanabilirsiniz.

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

Çıktı:

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

strcoll 'ü kullanabileceğinizi düşünüyorum:

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

Sonuç:

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

Sisteminizde nl_BE.utf8 yerel ayarına ihtiyacınız var:

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

Debian kullanıyorsanız, yereller eklemek için dpkg --reconfigure locales komutunu kullanabilirsiniz.

9
katma
CRT sahte uygulama nedeniyle utf-8 ile Windows'ta strcoll çalışmaz
katma yazar Enyby, kaynak
Bugüne kadar Temizleyici Çözüm.
katma yazar Jaison Erick, kaynak
Thai'nin PHP için çözümü 5.3 de temiz görünüyor
katma yazar Fy-, kaynak

Bu betik özel bir şekilde çözülmelidir. Umarım yardımcı olur. Mb_strtolower işlevine dikkat edin. Bunu kullanmanız gerekir, fonksiyon harfini duyarsız kılar. Strtolower işlevini kullanmadığımı nedeni, özel karakterlerle iyi çalışmadığıdır.

<?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: Üzgünüm. Son kodda birçok hata yaptım. Şimdi test edildi.

EDIT {2}: Çok baytlı işlevlere sahip her şey.

5
katma
Arama tablosu çözümü için +1.
katma yazar Billy ONeal, kaynak
Ne yazık ki bu işe yaramaz, çünkü $ a [$ i] tek bir char değil, dizeden tek bir byte döndürür.
katma yazar Leonid Shevtsov, kaynak
str_split , çok baytlı dizeleri de işlemez. :) Bkz. php.net/manual/en/function.mb -split.php # 99851
katma yazar Leonid Shevtsov, kaynak
sık sık strlen işlevini çalıştırmayın, sadece onları bir kez önceden çalıştırmanız gerekir ve her ikisinin de min değerini elde edebilirsiniz.
katma yazar hakre, kaynak
Önceden, evet haklıydın. Birkaç dakika önce algoritmayı değiştirdim. Str_split kullanarak çalışır.
katma yazar Jaison Erick, kaynak
Haklısın. Her yerde çok bayt olarak güncellendi.
katma yazar Jaison Erick, kaynak

Strcoll'a gelince, bu iyi bir fikirdi, ama işe yaramaz gibi görünüyor:

<?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

Bir süre önce "älph # bla" ifadesini "aelph" e çeviren bir UTF-8 - ASCII yazdım. -bla". Bunu, girişinizi sıralanabilir hale getirmek için "normalleştirmek" için kullanabilirsiniz. Temelde @Nick'in söylediğine benzer bir yedek.

Sıralamak için ayrı bir diziyi kullanmalısınız, çünkü bir usort() öğesindeki urlify() çağrısı çok fazla kaynak harcar. Deneyin

<?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 veya derlenmiş PECL'iniz varsa, @ Thai'nin çözümünü deneyin, tatlı görünün!

1
katma

Dizi boyunca döngü yapmak ve sıralamadan önce ingilizce karakterleri dönüştürmek için cazip olurdu. Örneğin.

<?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);
?>

Muhtemelen kullanılan işlem hızı/kaynakları açısından en iyisi değil. Ancak, kodu daha kolay anlamanızı sağlar.

Düzenle:

Şu an hakkında düşünürseniz, bir çeşit arama tablosu kullanmanız daha iyi olabilir:

<?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
katma
Dürüst olmak gerekirse, bu veri kümesine göre işe yarayan akla ilk gelen yerdi. Şimdi düşünmek, eğer veri kümesi diğer anormal karakterleri kullanacaksa, bir dönüşüm arama tablosu kullanmak daha iyi olabilir.
katma yazar Nick, kaynak
Neden nl_BE öneriyorsunuz? (Hollandaca konuşulan/Belçika'da yazılı olarak)
katma yazar middus, kaynak

Yerel çözümü kullanmak istiyorsanız, bunu önerebilirim

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');

Emin değilim, en iyi çözüm bu, ama benim için çalışıyor =)

1
katma
güzel, harika.
katma yazar Eir, kaynak
Php 7 ve yeni operatör "uzay gemisi" ile ilgili küçük güncelleme. Son koşulda 1 veya -1 için <=> kullanabilirsiniz.
katma yazar Amir Djaminov, kaynak