Du befindest Dich im Archiv vom ABAKUS Online Marketing Forum. Hier kannst Du Dich für das Forum mit den aktuellen Beiträgen registrieren.

PHP: Array-Sortierung mit Umlauten

Ajax, Hijax, Microformats, RDF, Markup, HTML, PHP, CSS, MySQL, htaccess, robots.txt, CGI, Java, Javascript usw.
Neues Thema Antworten
Synonym
PostRank 10
PostRank 10
Beiträge: 3708
Registriert: 09.08.2008, 02:55

Beitrag von Synonym » 17.11.2009, 17:43

Hallo zusammen,

ich stehe hier nun schon länger vor einem Problem, wo ich mir die Zähne ausbeiße und dennoch nicht weiter komme.

Aus der Datenbank werden 20 Datensätze abgefragt und sortiert nach Anzahl ( count() ). Diese 20 Datensätze benötige ich aber bei der Ausgabe sortiert nach der Bezeichnung. Soweit so gut...

Ein normales asort($array) sortiert mir auch die Werte entsprechend, nur eben die mit Sonderzeichen am Anfang nicht. ÄÖÜ stehen daher immer am Ende.

Seit PHP 4.4 gibt es ja das "asort($array, SORT_LOCALE_STRING)", besser gesagt, den Flag SORT_LOCALE_STRING. Eigentlich sollte es damit ja möglich sein. Doch auf meinem Testsystem (PHP5.2) sortiert der da irgendwas, aber keine Ahnung was. Schaut einfach nur bunt gewürfelt aus.

Dann heißt es, dass der Zeichensatz vom System genommen wird, bzw. man diesen per "setlocale (LC_ALL, 'de_DE@euro', 'de_DE.utf8', 'de_DE', 'de', 'ge');" vorgeben kann.
Egal ob nun mit oder ohne setlocal, der Zeichensatz ist "de_DE.utf8" und wird auch so angegeben wenn ich ihn abfrage.

So, nun stellt sich halt die Frage:
Wie kann ich das Array sortieren inkl. den Sonderzeichen??

Die Datenbank würde das schon richtig machen, doch die übernimmt ja die Sortierung nach count() und holt halt nur die 20 Datensätze aus den vorhandenen 30.000.

Hat jemand eine Idee?

Nachtrag:
Wenn ich im Array einen extra Wert mit "strtr($row->bezeichnung, array('Ä'=>'Ae','Ö'=>'Oe','Ü'=>'Ue'))" speichere, dann sortiert er auch richtig, aber irgendwie finde ich das Unfug. Unnötiges Ersetzen von Zeichen, größeres Array, nur wegen der Sortierung, etc... Geht das nicht anders?

Anzeige von ABAKUS

von Anzeige von ABAKUS »

SEO Consulting bei ABAKUS Internet Marketing
Erfahrung seit 2002
  • persönliche Betreuung
  • individuelle Beratung
  • kompetente Umsetzung

Jetzt anfragen: 0511 / 300325-0.


Velnias
PostRank 3
PostRank 3
Beiträge: 68
Registriert: 23.08.2009, 18:32
Wohnort: DE + LT

Beitrag von Velnias » 17.11.2009, 19:02

Hallo Synonym,

PHP macht das schon richtig. Das Ä kommt nach Z.
https://de.wikipedia.org/wiki/Deutsches_Alphabet

Anonymous

Beitrag von Anonymous » 17.11.2009, 19:09

Velnias hat geschrieben:Hallo Synonym,

PHP macht das schon richtig. Das Ä kommt nach Z.
https://de.wikipedia.org/wiki/Deutsches_Alphabet
na dann schau mal hier: https://de.wikipedia.org/wiki/Deutsches ... eihenfolge

8)

Synonym
PostRank 10
PostRank 10
Beiträge: 3708
Registriert: 09.08.2008, 02:55

Beitrag von Synonym » 17.11.2009, 19:42

Ja das meine ich aber auch. Ä kommt bei A. Ob sich das nun bei Ae einsortiert oder am Ende von A (Az) wäre mir noch egal, aber es gehört zu A.

Wie gesagt, die DB könnte es schon, wenn nicht schon eine andere Sortierung inkl. Limit da wäre. die mb_ Funktionen auch, aber da gibt es ja nicht zum Sortieren von Arrays ohne die Werte einzeln zu durchlaufen.

Also derzeit komme ich echt nur auf die Lösung mit dem strtr oder eben auf zwei DB-Anfragen. Einmal nur die IDs der 20 Datensätze und dann als nächstes die eigentlichen Werte nach Namen sortiert. Aber diese doppelgemoppel wollte ich eigentlich umgehen.

Hm, oder die Werte noch mal "sortierfreundlich" in die DB schreiben, aber wegen einer Handvoll (oder zwei) alle 30.000 nochmal speichern. Gefällt mir nicht.

Mork vom Ork
PostRank 9
PostRank 9
Beiträge: 2557
Registriert: 08.07.2008, 11:07
Wohnort: Aufm Friedhof.

Beitrag von Mork vom Ork » 17.11.2009, 21:25

Synonym hat geschrieben:Aus der Datenbank werden 20 Datensätze abgefragt und sortiert nach Anzahl ( count() ). Diese 20 Datensätze benötige ich aber bei der Ausgabe sortiert nach der Bezeichnung.
Das kannst du mit einer Unterabfrage machen, sofern es deine Datenbank unterstützt. Du verschachtelst einfach zwei select-Anweisungen, die innere holt dir die 20 Datensätze mit den meisten bzw. wenigsten Vorkommen und die äußere macht nichts weiter als nach Bezeichnung zu sortieren:

select * from (select bezeichnung,count(*) as n from tabelle group by bezeichnung order by n desc limit 20) as unterabfrage order by bezeichnung;
Dann heißt es, dass der Zeichensatz vom System genommen wird, bzw. man diesen per "setlocale (LC_ALL, 'de_DE@euro', 'de_DE.utf8', 'de_DE', 'de', 'ge');" vorgeben kann.
Egal ob nun mit oder ohne setlocal, der Zeichensatz ist "de_DE.utf8" und wird auch so angegeben wenn ich ihn abfrage.
Du solltest dir schon die Beschreibung von setlocale() durchlesen, man kann der Funktion nicht einfach einen Haufen Namen vor den Latz knallen und hoffen, dass irgendwas schon funktionieren wird.

Zum ersten ist de_DE@euro (iso-8859-15) ein anderer Zeichensatz als de_DE.utf8. Mit einer iso-8859-15-orientierten Sortierfunktion kannst du keine utf-8-kodierten Zeichen sortieren, jedenfalls nicht komplett, und umgekehrt natürlich auch nicht. Zum zweiten nimmt setlocale() aus so einer Liste den ersten Eintrag, der sich im System setzen lässt. Bei dir ist das offenbar de_DE.utf8 (merkwürdigerweise), deshalb kommt bei diesem Aufruf immer de_DE.utf8 raus.
Die Zeichenverarbeitung ist bei PHP von Anno Dazumal, du musst schon wissen, in welcher Kodierung du die Zeichen von der Datenbank bekommst und dann genau dieses mit setlocale() einstellen. Danach könnte man weitersehen - aber die Sortiererei geht ja auch direkt in der Datenbank und da gehört sie IMHO auch hin.

Synonym
PostRank 10
PostRank 10
Beiträge: 3708
Registriert: 09.08.2008, 02:55

Beitrag von Synonym » 18.11.2009, 10:00

Hi Mork vom Ork,

also Deine Lösung ist quasi der Subselect. Gut, das kannte ich, das geht auch. Auf dem Testsystem würde es zumindest gehen, aber es muss dann erst mal wieder eine Zeit lang (Wochen) auf mein eigentliches System zurück und da unterstützt es MySQL nicht. Daher auch mein gedanklicher Ansatz mit den zwei getrennten Queries, bei dem erst die IDs entsprechend der Anzahl sortiert werden und dann die Datensätze entsprechend den IDs nach Namen sortiert abgerufen werden.

Das mit dem setlocal() hat mich aber nun doch etwas erstaunt. Mehrere Werte können aber durchaus angegeben werden. Er nimmt dann, so wie Du sagst, den ersten der auf dem System verfügbar ist. Mein Beispiel im Post war etwas blöd gewählt, aber das würde so schon gehen und ist eigentlich bei mir grundsätzlich für den Wechsel zwischen Linux und Windows (lokal) gedacht. Und den utf-8 hatte ich extra weiter hinten aufgeführt um zu sehen, ob er nicht evtl. den de_DE@euro selektiert.

Aber das setlocal ist bei mir gar nicht erforderlich auf dem Server, denn der kennt nur uft-8. PHP, Apache, Mysql (Server/Client/Verbindung), die Dateien und alles andere sind dementsprechend eingerichtet / gespeichert.

"Mit einer iso-8859-15-orientierten Sortierfunktion kannst du keine utf-8-kodierten Zeichen sortieren"
Da hast Du recht. Daher ist es ja auch blöd, dass PHP noch immer solche Probleme macht. Alle anderen uft-8 betreffenden Bereiche sind aber gelöst und funktionieren fehlerfrei, nur nicht beim Sortieren von Arrays. Lustig deswegen ja auch, dass genau der Flag "SORT_LOCALE_STRING" das beheben und eine entsprechende Sortierung ermöglichen soll. Aber wie gesagt, da ist nichts sortiert, in keiner Art (Key / Value), eher gewürfelt oder wie ORDER BY RAND().

Also, dann werde ich mir wohl mal die beiden Versionen ansehen welche effektiver ist. Zweite Query oder strtr.

Danke und Gruß,
Ingo

Mork vom Ork
PostRank 9
PostRank 9
Beiträge: 2557
Registriert: 08.07.2008, 11:07
Wohnort: Aufm Friedhof.

Beitrag von Mork vom Ork » 18.11.2009, 11:58

Ich hab die setlocale()/asort()-Geschichte mal bei mir mit PHP 5.2 unter Linux ausprobiert, und zwar mit diesem Skript:

Code: Alles auswählen

$l = setlocale(LC_ALL, "de_DE.ISO-8859-15@euro");
# oder $l = setlocale(LC_ALL, "de_DE.UTF-8");
var_dump($l);

$l = setlocale(LC_ALL, 0);
var_dump($l);

$a = Array("a", "A", "c", "ä", "b", "ü", "S", "ö", "ß");
asort($a, SORT_LOCALE_STRING);
var_dump($a);
Das Skript ist utf-8-kodiert gespeichert.

Ohne setlocale() wird stur nach Bytewert sortiert (LC_COLLATE=C), das ist dann die Reihenfolge, mit der du dich derzeit rumschlagen musst:

Code: Alles auswählen

string(181) "LC_CTYPE=de_DE.ISO-8859-15@euro;LC_COLLATE=C;[rhabarberrhabarberrhabla..]"
array(9) {
  [1]=>
  string(1) "A"
  [6]=>
  string(1) "S"
  [0]=>
  string(1) "a"
  [4]=>
  string(1) "b"
  [2]=>
  string(1) "c"
  [8]=>
  string(2) "ß"
  [3]=>
  string(2) "ä"
  [7]=>
  string(2) "ö"
  [5]=>
  string(2) "ü"
}
Mit Umstellung auf de_DE.ISO-8859-15 ändert sich die Reihenfolge:

Code: Alles auswählen

string(22) "de_DE.ISO-8859-15@euro"
string(22) "de_DE.ISO-8859-15@euro"
array(9) {
  [0]=>
  string(1) "a"
  [1]=>
  string(1) "A"
  [7]=>
  string(2) "ö"
  [3]=>
  string(2) "ä"
  [8]=>
  string(2) "ß"
  [5]=>
  string(2) "ü"
  [4]=>
  string(1) "b"
  [2]=>
  string(1) "c"
  [6]=>
  string(1) "S"
}
Man beachte die Sortierung der Umlaute. Diese sind ja im Quellcode utf-8-kodiert und erscheinen mit der 8859-Kodierung als "À", "ÃŒ", "ö" und "Ã_". Sie sind also von diesem Standpunkt aus korrekt einsortiert, da sie mit à beginnen und dieses hinter a und A und vor b kommt.

Mit Umstellung auf de_DE.UTF-8 läuft es dann auch tatsächlich korrekt:

Code: Alles auswählen

string(11) "de_DE.UTF-8"
string(11) "de_DE.UTF-8"
array(9) {
  [0]=>
  string(1) "a"
  [1]=>
  string(1) "A"
  [3]=>
  string(2) "ä"
  [4]=>
  string(1) "b"
  [2]=>
  string(1) "c"
  [7]=>
  string(2) "ö"
  [6]=>
  string(1) "S"
  [8]=>
  string(2) "ß"
  [5]=>
  string(2) "ü"
}
Kurzum: setlocale() funktioniert, bei dir muss noch irgendwas anderes im Argen liegen.

Synonym
PostRank 10
PostRank 10
Beiträge: 3708
Registriert: 09.08.2008, 02:55

Beitrag von Synonym » 18.11.2009, 12:28

Hm, ok, dann muss ich da mal weitersehen was das sein kann.

Sobald ich das Flag SORT_LOCALE_STRING mit angebe, egal ob mit setlocal() oder ohne, ob mit gültigen Werten oder nicht, das Ergebnis ist immer wild gemischt.

setlocale (LC_COLLATE, 'de_DE.utf8');
uasort ($a, 'strcoll');
Würde seltsamerweise richtig sortieren, aber genau in umgekehrter Reihenfolge. Also auch nicht so wie im PHP-Handbuch, sondern beginnt mit Z und endet mit a. Selbiges auch mit strcmp anstelle dem strcoll.

"strcoll() legt dem Vergleich die aktuellen locale-Angabe zu Grunde. Ist deren Wert C oder POSIX, verhält sich diese Funktion äquivalent zu strcmp()."
Die ist bei mir aber immer "äquivalent".

Ich glaube ich werde mal das System wechseln und einen anderen Server zum testen nehmen....

Danke und Gruß,
Ingo

Antworten
  • Vergleichbare Themen
    Antworten
    Zugriffe
    Letzter Beitrag