Frage

ich currentyl keine Ahnung hat, wie ein Array zu sortieren, die UTF-8 kodierten Strings in PHP enthält. Das Array kommt von einem LDAP-Server so über eine Datenbank Sortierung (wäre kein Problem) keine Lösung. Die folgende nicht auf meiner Windows-Entwicklung Maschine arbeiten (obwohl ich glaube, würde, dass diese zumindest eine mögliche Lösung sein soll):

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

Die Ausgabe lautet:

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

Das ist völliger Unsinn. Mit 1252 als die Codepage für setlocale() einen anderen Ausgang gibt aber immer noch eine deutlich falsch ein:

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

Gibt es eine Möglichkeit ein Array mit UTF-8 zu sortieren Strings locale bewusst?

bemerkte nur, dass dieser PHP auf Windows-Problem zu sein scheint, als die Schnipsel mit de_DE.utf8 als locale verwendeten auf einem Linux-Rechner funktionieren. Dennoch ist eine Lösung für dieses spezielle Windows-Problem wäre schön ...

War es hilfreich?

Lösung 3

Schließlich kann dieses Problem nicht auf einfache Weise gelöst werden, ohne neu codierte Strings (UTF-8 → Windows 1252 oder ISO-8859-1), wie sie ΤΖΩΤΖΙΟΥ vorgeschlagen aufgrund eines offensichtlichen PHP Fehler von Huppie als entdeckt. Um das Problem zu fassen, habe ich den folgenden Code-Schnipsel, die deutlich zeigt, dass das Problem die strcoll ist () Funktion bei der Verwendung des 65001 von Windows-UTF-8-Zeichensatz.

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

Das Ergebnis ist:

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

Der gleiche Schnipsel funktioniert auf einem Linux-Rechner ohne Probleme die folgende Ausgabe erzeugt:

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

Das Snippet funktioniert auch, wenn Windows-1252 (ISO-8859-1) kodierten Strings (natürlich die mb_ * Codierungen und das Gebietsschema muss dann geändert werden).

Ich legte einen Fehlerbericht auf bugs.php.net : Bug # 46165 strcoll () nicht mit UTF-8-Strings auf Windows arbeiten. Wenn Sie das gleiche Problem auftreten, können Sie mit Ihrem Feedback, das PHP-Team auf der Bug-Report Seite geben (zwei andere, wahrscheinlich im Zusammenhang wurden Fehler klassifizieren als Schein - Ich glaube nicht, dass diese Bug ist falsche , -).

Vielen Dank an sie alle.

Andere Tipps

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

Prints:

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)

Die Collator Klasse wird in PECL intl Erweiterung definiert. Es ist mit PHP 5.3 Quellen verteilt, sondern möglicherweise deaktiviert für einige Builds. Z.B. in Debian ist es in Paket php5-intl.

Collator::compare ist nützlich für usort.

Update zu diesem Thema:

Auch wenn die Diskussion um dieses Problem ergeben, dass wir einen PHP-Fehler mit strcoll() und / oder setlocale() , ist dies eindeutig nicht der Fall. Das Problem ist vielmehr eine Beschränkung der Windows-CRT-Implementierung von setlocale() (PHPs setlocale() ist nur eine dünne Hülle um den CRT-Aufruf). Hier finden Sie ein Zitat des MSDN-Seite "setlocale, _wsetlocale" :

  

Die Menge der verfügbaren Sprachen,   Land / Region-Codes und Codepages   umfasst alle durch die diejenigen unterstützten   Win32 NLS API außer Codepages, die   benötigen mehr als zwei Bytes pro   Zeichen, wie beispielsweise UTF-7 und UTF-8. Wenn   Sie bieten eine Codepage wie UTF-7 oder   UTF-8, setlocaleQ fehl, Rückkehr   NULL. Der Satz von Sprache und   Land / Region-Codes unterstützt durch   setlocaleQ wird in Sprache aufgeführt und   Land / Region Strings.

Es ist daher unmöglich locale-aware String-Operationen innerhalb PHP auf Windows zu verwenden, wenn Strings Multi-Byte codiert ist.

Dies ist eine sehr komplexe Ausgabe rel="nofollow, da UTF-8 kodierten Daten kann jedes Unicode-Zeichen enthalten (dh Zeichen aus vielen 8-Bit-Kodierungen, die unterschiedlich zusammenstellen in verschiedenen Positionen).

Vielleicht, wenn Sie Ihre UTF-8-Daten in Unicode (nicht vertraut mit PHP Unicode-Funktionen, sorry) umgewandelt und dann normalisierten sie in NFD oder NFKD und dann das Sortieren auf Codepunkte könnten einige Sortierungs geben, die für Sie Sinn (dh „A“ vor dem „A“) machen würde.

die Links überprüfen I zur Verfügung gestellt.

EDIT: da Sie erwähnen, dass Sie Ihre Eingabedaten klar sind (ich nehme sie alle fallen in die „windows-1252“ Codepage), dann sollten Sie die folgende Umwandlung tun: UTF-8 → Unicode → Windows 1252, auf dem Windows-1252-codierte Daten zu tun, eine Art der "CP1252" locale auswählen.

Mit Ihrem Beispiel mit Codepage 1252 perfekt funktionierte hier auf meiner Windows-Entwicklungsmaschine.

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

... schnipp ...

Dies war mit PHP 5.2.6. btw.


Das obige Beispiel ist falsch , verwendet es ASCII-Kodierung anstelle von UTF-8. Ich habe die strcoll verfolgen () aufruft und schauen, was ich gefunden:

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

gibt:

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

habe ich ein paar Bug-Reports finden die gefälschtes ... Die beste Wette, die Sie haben, ist die Einreichung einen Bug-Bericht, den ich allerdings nehme ...

fanden diese folgende Hilfsfunktion alle Buchstaben einer Zeichenkette in ASCII-Buchstaben sehr hilfreich, hier zu konvertieren.

function _all_letters_to_ASCII($string) {
  return strtr(utf8_decode($string), 
    utf8_decode('ŠŒŽšœžŸ¥µÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýÿ'),
    'SOZsozYYuAAAAAAACEEEEIIIIDNOOOOOOUUUUYsaaaaaaaceeeeiiiionoooooouuuuyy');
}

Danach ein einfaches array_multisort() gibt Ihnen, was Sie wollen.

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

Natürlich kann man die Hilfsfunktion erweiterte Anforderungen fit machen. Aber jetzt, es sieht gut aus.

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

Ich bin mit dem gleichen Problem mit dem deutschen „Umlaute“ konfrontiert. Nach einigen Recherchen, arbeitete für mich:

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

Das Ergebnis:

  

Array
      (
      [0] => Ägypten
      [1] => England
      [2] => Frankreich
      [3] => Österreich
      [4] => Schweiz
      )

Ihre Sortierungs muss den Zeichensatz entsprechen. Da Ihre Daten UTF-8 kodiert ist, sollten Sie eine UTF-8 Sortierung verwenden. Es könnte auf verschiedenen Plattformen unterschiedlich benannt werden, aber eine gute Vermutung wäre de_DE.utf8 werden.

Auf UNIX-Systemen können Sie eine Liste der aktuell installierten locales mit dem Befehl get

locale -a
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top