utiles - ¿Algoritmo de clasificación natural en PHP con soporte para Unicode?
sistemas naturales ejemplos (5)
¿Es posible ordenar una matriz con caracteres Unicode / UTF-8 en PHP usando un algoritmo de orden natural? Por ejemplo (el orden en esta matriz está ordenado correctamente):
$array = array
(
0 => ''Agile'',
1 => ''Ágile'',
2 => ''Àgile'',
3 => ''Âgile'',
4 => ''Ägile'',
5 => ''Ãgile'',
6 => ''Test'',
);
Si intento con asort ($ array) obtengo el siguiente resultado:
Array
(
[0] => Agile
[6] => Test
[2] => Àgile
[1] => Ágile
[3] => Âgile
[5] => Ãgile
[4] => Ägile
)
Y usando natsort ($ array):
Array
(
[2] => Àgile
[1] => Ágile
[3] => Âgile
[5] => Ãgile
[4] => Ägile
[0] => Agile
[6] => Test
)
¿Cómo puedo implementar una función que devuelva el orden de resultado correcto (0, 1, 2, 3, 4, 5, 6) bajo PHP 5? Todas las funciones de cadena de múltiples bytes (mbstring, iconv, ...) están disponibles en mi sistema.
EDITAR: Quiero natsort () los valores, no las claves; la única razón por la que estoy definiendo explícitamente las claves (y utilizando asort () en lugar de sort ()) es para facilitar el trabajo de averiguar dónde se clasifican Los valores de Unicode salieron mal.
La pregunta no es tan fácil de responder como parece a primera vista. Esta es una de las áreas donde la falta de compatibilidad con Unicode de PHP lo golpea con toda su fuerza.
Frist of all natsort()
como lo sugieren otros carteles, no tiene nada que ver con la ordenación de matrices del tipo que desea ordenar. Lo que está buscando es un mecanismo de clasificación que tenga en cuenta la configuración regional, ya que las cadenas de clasificación con caracteres extendidos son siempre una cuestión del idioma utilizado. Tomemos, por ejemplo, el alemán: A y Ä a veces se pueden clasificar como si fueran la misma letra (DIN 5007/1), y a veces Ä se pueden clasificar como en realidad era "AE" (DIN 5007/2). En sueco, en contraste, Ä viene al final del alfabeto.
Si no usa Windows, tiene suerte ya que PHP proporciona algunas funciones para esto. Al usar una combinación de setlocale()
, usort()
, strcoll()
y la configuración regional UTF-8 correcta para su idioma, obtiene algo como esto:
$array = array(''Àgile'', ''Ágile'', ''Âgile'', ''Ãgile'', ''Ägile'', ''Agile'', ''Test'');
$oldLocal = setlocale(LC_COLLATE, ''<<your_RFC1766_language_code>>.utf8'');
usort($array, ''strcoll'');
setlocale(LC_COLLATE, $oldLocal);
Tenga en cuenta que es obligatorio utilizar la variante de configuración regional UTF-8 para ordenar las cadenas UTF-8. Restablecimiento de la configuración regional en el ejemplo anterior a su valor original, ya que establecer una configuración regional utilizando setlocale()
puede introducir efectos secundarios en otras secuencias de comandos PHP en ejecución. Consulte el manual de PHP para obtener más detalles.
Cuando utiliza una máquina con Windows, actualmente no hay una solución para este problema y no habrá ninguna antes de PHP 6, supongo. Por favor vea mi propia question sobre SO que question este problema específico.
Luché con Asort con este problema.
Clasificación:
Array
(
[xa] => África
[xo] => Australasia
[cn] => China
[gb] => Reino Unido
[us] => Estados Unidos
[ae] => Emiratos Árabes Unidos
[jp] => Japón
[lk] => Sri Lanka
[xe] => Europa Del Este
[xw] => Europa Del Oeste
[fr] => Francia
[de] => Alemania
[be] => Bélgica
[nl] => Holanda
[es] => España
)
Ponga África al final. Lo resolví con este pequeño y sucio código (que se ajusta a mi propósito y a mi calendario):
$sort = array();
foreach($retval AS $key => $value) {
$v = str_replace(''ä'', ''a'', $value);
$v = str_replace(''Ä'', ''A'', $v);
$v = str_replace(''Á'', ''A'', $v);
$v = str_replace(''é'', ''e'', $v);
$v = str_replace(''ö'', ''o'', $v);
$v = str_replace(''ó'', ''o'', $v);
$v = str_replace(''Ö'', ''O'', $v);
$v = str_replace(''ü'', ''u'', $v);
$v = str_replace(''Ü'', ''U'', $v);
$v = str_replace(''ß'', ''S'', $v);
$v = str_replace(''ñ'', ''n'', $v);
$sort[] = "$v|$key|$value";
}
sort($sort);
$retval = array();
foreach($sort AS $value) {
$arr = explode(''|'', $value);
$retval[$arr[1]] = $arr[2];
}
También tengo otra solución para que setlocale
no funcione y no tenga el módulo intl
habilitado:
// The array to be sorted
$countries = array(
''AT'' => Österreich,
''DE'' => Deutschland,
''CH'' => Schweiz,
);
// Extend this array to your needs.
$utf_sort_map = array(
"ä" => "a",
"Ä" => "A",
"Å" => "A",
"ö" => "o",
"Ö" => "O",
"ü" => "u",
"Ü" => "U",
);
uasort($my_array, function($a, $b) use ($utf_sort_map) {
$initial_a = mb_substr($a, 0, 1);
$initial_b = mb_substr($b, 0, 1);
if (isset($utf_sort_map[$initial_a]) || isset($utf_sort_map[$initial_b])) {
if (isset($utf_sort_map[$initial_a])) {
$initial_a = $utf_sort_map[$initial_a];
}
if (isset($utf_sort_map[$initial_b])) {
$initial_b = $utf_sort_map[$initial_b];
}
if ($initial_a == $initial_b) {
return mb_substr($a, 1) < mb_substr($b, 1) ? -1 : 1;
}
else {
return $initial_a < $initial_b ? -1 : 1;
}
}
return $a < $b ? -1 : 1;
});
¡Dado en el clavo!
$array = array(''Ägile'', ''Ãgile'', ''Test'', ''カタカナ'', ''かたかな'', ''Ágile'', ''Àgile'', ''Âgile'', ''Agile'');
function Sortify($string)
{
return preg_replace(''~&([a-z]{1,2})(acute|cedil|circ|grave|lig|orn|ring|slash|tilde|uml);~i'', ''$1'' . chr(255) . ''$2'', htmlentities($string, ENT_QUOTES, ''UTF-8''));
}
array_multisort(array_map(''Sortify'', $array), $array);
Salida:
Array
(
[0] => Agile
[1] => Ágile
[2] => Âgile
[3] => Àgile
[4] => Ãgile
[5] => Ägile
[6] => Test
[7] => かたかな
[8] => カタカナ
)
Aun mejor:
if (extension_loaded(''intl'') === true)
{
collator_asort(collator_create(''root''), $array);
}
Gracias a @tchrist!
natsort($array);
$array = array_values($array);