vocali - php ordinare array




Come ordinare un array di stringhe UTF-8? (6)

Aggiornamento su questo problema:

Anche se la discussione su questo problema ha rivelato che avremmo potuto scoprire un bug PHP con strcoll() e / o setlocale() , questo non è chiaramente il caso. Il problema è piuttosto una limitazione dell'implementazione di CRT di Windows di setlocale() (PHP setlocale() è solo un sottile involucro attorno alla chiamata CRT). Quanto segue è una citazione della setlocale() :

L'insieme di lingue disponibili, codici paese / regione e code page include tutti quelli supportati dall'API NLS Win32 eccetto le code page che richiedono più di due byte per carattere, come UTF-7 e UTF-8. Se fornisci una tabella codici come UTF-7 o UTF-8, setlocale fallirà, restituendo NULL. Il set di codici lingua e paese / regione supportati da setlocale è elencato in Language e Country / Region Strings.

Pertanto, è impossibile utilizzare le operazioni sulle stringhe con impostazioni locali all'interno di PHP su Windows quando le stringhe sono codificate a più byte.

Attualmente non ho idea di come ordinare un array che contiene stringhe con codifica UTF-8 in PHP. L'array proviene da un server LDAP, quindi l'ordinamento tramite un database (non sarebbe un problema) non è una soluzione. Quanto segue non funziona sul mio computer di sviluppo Windows (anche se penserei che questa dovrebbe essere almeno una possibile soluzione):

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

L'output è:

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

Questo non ha senso. L'uso di 1252 come codepage per setlocale() fornisce un altro output ma ancora chiaramente sbagliato:

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

C'è un modo per ordinare un array con le impostazioni locali delle stringhe UTF-8?

Ho appena notato che questo sembra essere un problema PHP su Windows, dato che lo stesso frammento con de_DE.utf8 usato come locale funziona su una macchina Linux. Tuttavia, una soluzione per questo problema specifico di Windows sarebbe bella ...


Alla fine questo problema non può essere risolto in modo semplice senza utilizzare stringhe ricodificate (UTF-8 → Windows-1252 o ISO-8859-1) come suggerito da ΤΖΩΤΖΙΟΥ a causa di un ovvio bug di PHP scoperto da Huppie. Per riassumere il problema, ho creato il seguente frammento di codice che dimostra chiaramente che il problema è la funzione strcoll () quando si utilizza la codepage 65001 Windows-UTF-8.

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

Il risultato è:

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

Lo stesso frammento funziona su una macchina Linux senza problemi producendo il seguente output:

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

Lo snippet funziona anche quando si usano stringhe codificate Windows-1252 (ISO-8859-1) (ovviamente le codifiche mb_ * e le impostazioni internazionali devono essere modificate).

Ho archiviato un bug report su bugs.php.net : Bug # 46165 strcoll () non funziona con le stringhe UTF-8 su Windows . Se riscontri lo stesso problema, puoi dare il tuo feedback al team di PHP sulla pagina di bug-report (altri due, probabilmente correlati, i bug sono stati classificati come fasulli - non penso che questo bug sia fasullo ;-).

Grazie a tutti voi.


La tua fascicolazione deve corrispondere al set di caratteri. Poiché i tuoi dati sono codificati in UTF-8, dovresti utilizzare una regola di confronto UTF-8. Potrebbe essere chiamato diversamente su piattaforme diverse, ma una buona ipotesi sarebbe de_DE.utf8 .

Sui sistemi UNIX, è possibile ottenere un elenco di localizzazioni attualmente installate con il comando

locale -a

Mi trovo di fronte allo stesso problema con il tedesco "Umlaute". Dopo alcune ricerche, questo ha funzionato per me:

$laender =array("Österreich", "Schweiz", "England", "France", "Ägypten");  
$laender = array_map("utf8_decode", $laender);  
setlocale(LC_ALL,"[email protected]", "de_DE", "deu_deu");  
sort($laender, SORT_LOCALE_STRING);  
$laender = array_map("utf8_encode", $laender);  
print_r($laender);

Il risultato:

schieramento
(
[0] => Ägypten
[1] => Inghilterra
[2] => Francia
[3] => Österreich
[4] => Schweiz
)


Usando il tuo esempio con codepage 1252 ha funzionato perfettamente qui sul mio computer di sviluppo Windows.

$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 ...

Questo era con PHP 5.2.6. btw.

L'esempio sopra è sbagliato , usa la codifica ASCII invece di UTF-8. Ho rintracciato le chiamate strcoll () e guardo cosa ho trovato:

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

dà:

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

Ho trovato alcune segnalazioni di bug che sono state contrassegnate come bogus ... La migliore scommessa che hai è quella di presentare un bug-report, suppongo però ...


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

stampe:

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)

La classe Collator è definita nell'estensione intl PECL . È distribuito con sorgenti PHP 5.3 ma potrebbe essere disabilitato per alcune build. Ad esempio in Debian è nel pacchetto php5-intl.

Collator::compare è utile per usort .





utf-8