UTF-8 dizeleri bir dizi sıralamak nasıl?

7 Cevap php

I currentyl have no clue on how to sort an array which contains UTF-8 encoded strings in PHP. The array comes from a LDAP server so sorting via a database (would be no problem) is no solution. The following does not work on my windows development machine (although I'd think that this should be at least a possible solution):

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

Çıktısı:

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"
}

Bu tam bir saçmalık. Için kod sayfası olarak 1252 kullanarak setlocale() Başka bir çıkış verir ama yine de açıkça yanlış bir:

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 dizeleri ile bir dizi sıralamak için bir yol var farkında yerel ayara mı?

Yerel bir Linux makine üzerinde çalışır gibi Just noted that this seems to be PHP on Windows problem, as the same snippet with de_DE.utf8 kullanılır. Yine de bu, Windows-özel sorun için bir çözüm güzel olurdu ...

7 Cevap

Eventually this problem cannot be solved in a simple way without using recoded strings (UTF-8 → Windows-1252 or ISO-8859-1) as suggested by ΤΖΩΤΖΙΟΥ due to an obvious PHP bug as discovered by Huppie. To summarize the problem, I created the following code snippet which clearly demonstrates that the problem is the strcoll() function when using the 65001 Windows-UTF-8-codepage.

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

Sonuç:

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"
  [...]

Aynı pasajı aşağıdaki çıktıyı üreten sorunsuz bir Linux makine üzerinde çalışır:

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"
  [...]

(ISO-8859-1), kodlanmış dizeleri (tabii MB_ * kodlamaları ve yerel daha sonra değiştirilmesi gerekir), Windows-1252 kullanırken pasajı da çalışır.

Bug #46165 strcoll() does not work with UTF-8 strings on Windows: I bugs.php.net üzerinde bir hata raporu yayımlandı. Eğer aynı sorunu yaşıyorsanız, siz (diğer iki, muhtemelen ilgili, böcek olarak sınıflandırılmıştır bogus hata raporu sayfada PHP ekibine geri bildirim verebilir - Ben sanmıyorum ki bu hata bogus ;-) olduğunu.

Hepinize teşekkürler.

$a = array( 'Кръстев', 'Делян1', 'делян1', 'Делян2', 'делян3', 'кръстев' );
$col = new \Collator('bg_BG');
$col->asort( $a );
var_dump( $a );

Baskılar:

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 class PECL intl extension tanımlanmıştır. PHP 5.3 kaynakları ile dağıtılan ancak bazı yapılar için devre dışı bırakılmış olabilir. Örneğin Debian bu paket php5-Intl olduğunu.

Collator::compare usort için yararlıdır.

Bu konuda güncelleyin:

Bu soruna tartışma biz strcoll() and/or setlocale() , this is clearly not the case. The problem is rather a limitation of the Windows CRT implementation of setlocale() (PHPs setlocale() sadece ince bir sarıcı ile bir PHP hata keşfettim olabileceği ortaya rağmen CRT çağrısı etrafında). Aşağıda, bir atıf olan MSDN page "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.

Bu nedenle dizeleri çoklu-bayt kodlanmış olduğunda Windows üzerinde PHP içinde yerele farkında dize işlemlerini kullanmak mümkün değildir.

Bu çok karmaşık bir issue, UTF-8 kodlanmış veriler herhangi bir Unicode karakter (Farklı yerlerde farklı harmanlamak çok 8-bit kodlamaları itibaren yani karakter) içerebilir beri.

Unicode (PHP unicode fonksiyonlarına aşina değil, üzgünüm) içine UTF-8 veri dönüştürülür ve ardından NFD or NFKD içine normalleştirilmiş ve ardından kod noktaları sıralama (sizin için mantıklı olur biraz harmanlama verebilir belki eğer "Ä") önce, yani "A".

Ben sağlanan bağlantıları kontrol edin.

EDIT: Eğer giriş verileri (ben "windows-1252" kod sayfasında hepsi düşmek varsayalım) açık olduğundan söz beri, daha sonra aşağıdaki dönüşüm yapmak gerekir: UTF-8 Unicode → Windows 1252, →, hangi Windows-1252 kodlanmış veriler "CP1252" yerel ayarı seçtikten bir tür yapmak.

Sıralama düzeni dile bağlı olduğunu unutmayın. Alman, onlar aynı mektubu sanki A ve Ä bazen sıralanabilir, ve aslında "AE" olduğu gibi bazen Ä sıralanabilir.

Ancak, alfabenin sonunda İsveç gelir.

Carl

Kod sayfası 1252 ile örneği kullanarak benim windows geliştirme makinede burada gayet çalıştı.

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

...snip...

This was with PHP 5.2.6. btw.


The above example is wrong, it uses ASCII encoding instead of UTF-8. I did trace the strcoll() calls and look what I found:

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

verir:

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

I did find some bug reports which have been flagged being bogus... The best bet you have is filing a bug-report I suppose though...

Sizin harmanlama karakter kümesi eşleşmesi gerekir. Veri UTF-8 kodlanmış olduğundan, UTF-8 harmanlama kullanmalısınız. Farklı platformlarda farklı adlandırılmış olabilir, ama iyi bir tahminim olurdu de_DE.utf8.

UNIX sistemlerinde, komut ile şu anda yüklü yerel bir listesini alabilirsiniz

locale -a