Pregunta

Yo currentyl no tienen ni idea acerca de cómo ordenar una matriz que contiene la codificación UTF-8 cadenas en PHP.La matriz proviene de un servidor LDAP para la clasificación a través de una base de datos (no sería un problema) no es la solución.La siguiente no funciona en mi windows desarrollo de la máquina (aunque me gustaría pensar que este debe ser de al menos una posible solución):

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

El resultado es:

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

Esto es una completa tontería.El uso de 1252 como la página de códigos setlocale() da otra salida, pero todavía claramente equivocado:

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

Es allí una manera de ordenar un array con UTF-8 cadenas de configuración regional consciente?

Acabamos de señalar que este parece ser PHP en Windows problema, como el mismo fragmento con de_DE.utf8 utilizado como local funciona en una máquina Linux.Sin embargo, una solución para este Windows-problema específico sería bueno...

¿Fue útil?

Solución 3

Finalmente, este problema no puede ser resuelto de una manera sencilla sin necesidad de utilizar recodificado cadenas de caracteres (UTF-8 → Windows-1252 o ISO-8859-1) como se sugiere por ΤΖΩΤΖΙΟΥ debido a un evidente error de PHP como descubierto por Huppie.Para resumir el problema, he creado el siguiente fragmento de código que demuestra claramente que el problema es el strcoll() función cuando se utiliza el 65001 Windows-UTF-8-página de códigos.

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

El resultado es:

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

El mismo fragmento de código que funciona en una máquina Linux sin ningún tipo de problemas para producir la siguiente salida:

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

El fragmento de código también funciona cuando se usa Windows-1252 (ISO-8859-1) cadenas codificadas (por supuesto, la mb_* codificaciones y la configuración regional debe ser cambiado a continuación).

He presentado un informe de fallo en bugs.php.net: Error #46165 strcoll() no funciona con cadenas de UTF-8 en Windows.Si tienes el mismo problema, puede dar sus comentarios al equipo de PHP en los reportes de errores de la página (los otros dos, probablemente relacionado, los errores han sido clasificados como falso - No creo que este error es falso ;-).

Gracias a todos ustedes.

Otros consejos

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

Impresiones:

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)

El Collator la clase se define en PECL extensión intl.Se distribuye con PHP 5.3 fuentes, pero puede ser desactivado para algunas versiones.E. g.en Debian se encuentra en el paquete php5-intl .

Collator::compare es útil para usort.

Actualización sobre este tema:

Aunque la discusión en torno a este problema revelado que podría haber descubierto un error de PHP con strcoll() y/o setlocale(), este es claramente el caso.El problema es más bien una limitación de las Ventanas de la CRT de la aplicación de setlocale() (PHPs setlocale() es sólo una envoltura alrededor de la CRT de la llamada).La siguiente es una cita de la Página de MSDN "setlocale, _wsetlocale":

El conjunto de los idiomas disponibles, país/región de los códigos, y las páginas de código incluye todos los admitidos por el NLS de Win32 API excepto el código de las páginas que requieren más de dos bytes por carácter, tales como UTF-7 y UTF-8.Si proporcionar una página de código como UTF-7 o UTF-8, setlocale va a fallar, volver NULL. El conjunto de la lengua y la país/región códigos apoyado por setlocale aparece en el Lenguaje y País/Región Cadenas.

Por lo tanto, es imposible usar la configuración regional de la cadena de operaciones dentro de PHP en Windows cuando son las cadenas de caracteres multi-byte codificado.

Este es un muy complejo problema, desde codificado en UTF-8 datos puede contener cualquier carácter Unicode (es decir,los personajes de muchas de 8 bits codificaciones que se recopile de manera diferente en diferentes lugares).

Tal vez si convierte los datos de UTF-8 en Unicode (no familiarizado con PHP las funciones unicode, lo siento) y, a continuación, normalizado en DFN o NFKD y luego de la clasificación por puntos de código podría dar alguna intercalación que tendría sentido para usted (es decir, "Un" antes de "Ä").

Compruebe los enlaces que he proporcionado.

EDITAR:ya que usted menciona que los datos de entrada son claras (supongo que todos caen en el "windows-1252" de la página de códigos), entonces usted debe hacer la siguiente conversión:UTF-8 → Unicode → Windows-1252, en la que Windows-1252 datos codificados de hacer una especie de seleccionar la "CP1252" configuración regional.

Usando tu ejemplo con la página de códigos 1252 funcionado perfectamente bien aquí en mi windows máquina de desarrollo.

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

Esto fue con PHP 5.2.6.btw.


El ejemplo anterior es mal, utiliza la codificación ASCII en lugar de UTF-8.Hice traza de la strcoll() llama y mira lo que he encontrado:

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

da:

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

Encontré algunos los informes de error que se han marcado se falso...La mejor apuesta es la presentación de un error-informe supongo, aunque...

Yo encuentra este siguiente función auxiliar para convertir todas las letras de una cadena ASCII de las letras muy útil aquí.

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

Después de que un simple array_multisort() te da lo que quieres.

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

Por supuesto, usted puede hacer la función de ayudante de ajuste necesidades más avanzadas.Pero por ahora, se ve bastante bien.

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

Estoy enfrentado con el mismo problema con el alemán "Umlaute".Después de algunas investigaciones, esto funcionó para mí:

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

El resultado:

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

Su intercalación debe coincidir con el conjunto de caracteres.Porque la información es codificada en UTF-8, usted debe utilizar una codificación UTF-8 de intercalación.Ella podría tener un nombre diferente en diferentes plataformas, pero una buena conjetura sería de_DE.utf8.

En los sistemas UNIX, usted puede obtener una lista de los actualmente instalados configuraciones regionales con el comando

locale -a
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top