mb_convert_encoding - utf8 encode php
¿Cómo ordenar una matriz de cadenas UTF-8? (8)
No tengo ni idea de cómo ordenar una matriz que contiene cadenas codificadas en UTF-8 en PHP. La matriz proviene de un servidor LDAP, por lo que ordenarla a través de una base de datos (no sería un problema) no es una solución. Lo siguiente no funciona en mi máquina de desarrollo de Windows (aunque creo que esta debería ser 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 tontería completa. El uso de 1252 como la página de códigos para setlocale()
da otra salida, pero sigue siendo claramente errónea:
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"
}
¿Hay alguna forma de ordenar una matriz con cadenas locales UTF-8?
Solo observé que esto parece ser problema de PHP en Windows, ya que el mismo fragmento con de_DE.utf8
utilizado como locale funciona en una máquina Linux. Sin embargo, una solución para este problema específico de Windows sería agradable ...
Actualización sobre este tema:
Aunque la discusión sobre este problema reveló que podríamos haber descubierto un error de PHP con strcoll()
y / o setlocale()
, claramente este no es el caso. El problema es más bien una limitación de la implementación de Windows CRT de setlocale()
(PHPs setlocale()
es solo una envoltura delgada alrededor de la llamada CRT). La siguiente es una cita de la setlocale() :
El conjunto de idiomas disponibles, códigos de país / región y páginas de códigos incluye todos aquellos admitidos por la API de Win32 NLS, excepto las páginas de códigos que requieren más de dos bytes por carácter, como UTF-7 y UTF-8. Si proporciona una página de códigos como UTF-7 o UTF-8, setlocale fallará y devolverá NULL. El conjunto de códigos de idioma y país / región soportados por setlocale se enumera en Idioma y cadenas de país / región.
Por lo tanto, es imposible utilizar operaciones de cadenas con reconocimiento de localización en PHP en Windows cuando las cadenas están codificadas en varios bytes.
Este es un issue muy complejo, ya que los datos codificados en UTF-8 pueden contener cualquier carácter Unicode (es decir, caracteres de muchas codificaciones de 8 bits que se clasifican de forma diferente en diferentes configuraciones regionales).
Quizás si convirtieras tus datos UTF-8 en Unicode (no está familiarizado con las funciones PHP Unicode, perdón) y luego los normalices en NFD o NFKD y luego clasifiques en puntos de código, podrías dar una intercalación que tenga sentido para ti (es decir, "A"). antes de").
Verifique los enlaces que brindé.
EDITAR: como mencionas que tus datos de entrada son claros (supongo que todos están en la página de códigos "windows-1252"), entonces debes hacer la siguiente conversión: UTF-8 → Unicode → Windows-1252, en el que Windows-1252 los datos codificados hacen un orden seleccionando la configuración regional "CP1252".
Eventualmente, este problema no se puede resolver de una manera sencilla sin usar cadenas grabadas (UTF-8 → Windows-1252 o ISO-8859-1) como sugiere ΤΖΩΤΖΙΟΥ debido a un error evidente de PHP como lo descubrió Huppie. Para resumir el problema, creé el siguiente fragmento de código que demuestra claramente que el problema es la función strcoll () cuando se utiliza la página de códigos 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);
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 funciona en una máquina Linux sin ningún problema produciendo el siguiente resultado:
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 también funciona cuando se usan cadenas codificadas en Windows-1252 (ISO-8859-1) (por supuesto, las codificaciones mb_ * y la configuración regional se deben cambiar a continuación).
Archivé un informe de error en bugs.php.net : Bug # 46165 strcoll () no funciona con cadenas UTF-8 en Windows . Si tiene el mismo problema, puede dar su opinión al equipo de PHP en la página de informe de fallas (otros dos errores, probablemente relacionados, han sido clasificados como falsos ; no creo que este error sea falso ;-).
Gracias a todos ustedes.
Me enfrento 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:
Formación
(
[0] => Ägypten
[1] => Inglaterra
[2] => Francia
[3] => Österreich
[4] => Schweiz
)
Tu colación debe coincidir con el conjunto de caracteres. Dado que sus datos están codificados en UTF-8, debe usar una intercalación UTF-8. Se podría nombrar de manera diferente en diferentes plataformas, pero una buena suposición sería de_DE.utf8
.
En los sistemas UNIX, puede obtener una lista de las configuraciones regionales instaladas actualmente con el comando
locale -a
Usando su ejemplo con la página de códigos 1252 funcionó perfectamente bien aquí en mi máquina de desarrollo de 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);
...recorte...
Esto fue con PHP 5.2.6. por cierto.
El ejemplo anterior es incorrecto , usa codificación ASCII en lugar de UTF-8. Sí rastreé las llamadas de strcoll () y mire lo que encontré: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 informes de errores que han sido marcados como bogus ... Sin embargo, la mejor apuesta que tienes es presentar un informe de errores, supongo ...
Encontré esta siguiente función auxiliar para convertir todas las letras de una cadena en letras ASCII muy útiles aquí.
function _all_letters_to_ASCII($string) {
return strtr(utf8_decode($string),
utf8_decode(''ŠŒŽšœžŸ¥µÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýÿ''),
''SOZsozYYuAAAAAAACEEEEIIIIDNOOOOOOUUUUYsaaaaaaaceeeeiiiionoooooouuuuyy'');
}
Después de eso, un simple array_multisort()
le proporciona lo que desea.
$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, puede hacer que la función auxiliar se ajuste a las 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"
}
$a = array( ''Кръстев'', ''Делян1'', ''делян1'', ''Делян2'', ''делян3'', ''кръстев'' );
$col = new /Collator(''bg_BG'');
$col->asort( $a );
var_dump( $a );
Huellas dactilares:
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 clase Collator
se define en la extensión PICL intl . Se distribuye con las fuentes de PHP 5.3, pero puede estar deshabilitado para algunas compilaciones. Por ejemplo, en Debian está en el paquete php5-intl.
Collator::compare
es útil para usort
.