Domanda

I currentyl non hanno idea di come ordinare un array che contiene stringhe codificate 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 sulla mia macchina per lo sviluppo di Windows (anche se penso 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"
}

Questa è una totale assurdità. 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"
}

Esiste un modo per ordinare un array con impostazioni locali delle stringhe UTF-8?

Ho appena notato che questo sembra essere un problema PHP su Windows, poiché 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 piacevole ...

È stato utile?

Soluzione 3

Alla fine questo problema non può essere risolto in modo semplice senza usare stringhe ricodificate (UTF-8 ? Windows-1252 o ISO-8859-1) come suggerito da ??O????? a causa di un evidente bug 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 tabella codici 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 utilizzano stringhe con codifica Windows-1252 (ISO-8859-1) (ovviamente le codifiche mb_ * e le impostazioni internazionali devono essere modificate).

Ho presentato una segnalazione di bug 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 PHP sulla pagina di segnalazione dei bug (altri due bug, probabilmente correlati, sono stati classificati come fasulli - Non credo che questo il bug è falso ;-).

Grazie a tutti voi.

Altri suggerimenti

$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 in Estensione PECL intl . È distribuito con fonti PHP 5.3 ma potrebbe essere disabilitato per alcune build. Per esempio. in Debian è nel pacchetto php5-intl.

Collator :: compare è utile per usort .

Aggiornamento su questo problema:

Anche se la discussione su questo problema ha rivelato che avremmo potuto scoprire un bug PHP con strcoll () e / o < code> setlocale () , chiaramente non è così. Il problema è piuttosto una limitazione dell'implementazione CRT di Windows di setlocale () (PHPs setlocale () è solo un sottile wrapper attorno alla chiamata CRT). Quella che segue è una citazione della pagina MSDN " setlocale, _wsetlocale " :

  

L'insieme delle lingue disponibili,   codici paese / regione e pagine codici   include tutti quelli supportati dal   API NLS Win32 tranne le pagine di codice 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à, tornando   NULL. Il set di lingua e   codici paese / regione supportati da   setlocale è elencato in Lingua e   Stringhe Paese / regione.

È pertanto impossibile utilizzare le operazioni di stringa compatibili con le impostazioni locali in PHP su Windows quando le stringhe sono codificate a più byte.

Questo è un molto complesso , dato che i dati codificati UTF-8 possono contenere qualsiasi carattere Unicode (ad esempio caratteri di molte codifiche a 8 bit che si differenziano in modo diverso in diverse lingue).

Forse se hai convertito i tuoi dati UTF-8 in Unicode (non hai familiarità con le funzioni unicode di PHP, scusami) e poi li hai normalizzati in NFD o NFKD e quindi l'ordinamento su punti di codice potrebbe dare un po 'di confronto che avrebbe senso per te (es. " A " prima di " Ä ").

Controlla i link che ho fornito.

MODIFICA: poiché dici che i tuoi dati di input sono chiari (suppongo che rientrino tutti nella tabella codici "windows-1252"), allora dovresti fare la seguente conversione: UTF-8 ? Unicode ? Windows-1252, su quali dati codificati Windows-1252 eseguono un ordinamento selezionando il "CP1252" locale.

Usando il tuo esempio con la tabella codici 1252 ha funzionato perfettamente qui sulla mia macchina per lo sviluppo di 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 , utilizza la codifica ASCII anziché UTF-8. Ho tracciato le chiamate strcoll () e ho visto quello che 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 fasullo ... La scommessa migliore che hai è presentare una segnalazione di bug, suppongo però ...

I ho trovato utile la seguente funzione di supporto per convertire qui tutte le lettere di una stringa in lettere ASCII.

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

Dopodiché un semplice array_multisort () ti dà quello che vuoi.

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

Ovviamente puoi adattare la funzione di supporto alle esigenze più avanzate. Ma per ora, sembra abbastanza buono.

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

Sono 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,"de_DE@euro", "de_DE", "deu_deu");  
sort($laender, SORT_LOCALE_STRING);  
$laender = array_map("utf8_encode", $laender);  
print_r($laender);

Il risultato:

  

Array
      (
      [0] = > & # 196; gypten
      [1] = > Inghilterra
      [2] = > Francia |       [3] = > & # 214; sterreich
      [4] = > Schweiz
      )

Le regole di confronto devono corrispondere al set di caratteri. Poiché i tuoi dati sono codificati UTF-8, dovresti usare un confronto UTF-8. Potrebbe essere nominato in modo diverso su piattaforme diverse, ma una buona ipotesi sarebbe de_DE.utf8 .

Sui sistemi UNIX, è possibile ottenere un elenco delle impostazioni locali attualmente installate con il comando

locale -a
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top