提问人:Alix Axel 提问时间:5/7/2009 最后编辑:hippietrailAlix Axel 更新时间:1/13/2016 访问量:12309
PHP中支持Unicode的自然排序算法?
Natural sorting algorithm in PHP with support for Unicode?
问:
是否可以使用自然顺序算法在 PHP 中对带有 Unicode / UTF-8 字符的数组进行排序?例如(此数组中的顺序正确排序):
$array = array
(
0 => 'Agile',
1 => 'Ágile',
2 => 'Àgile',
3 => 'Âgile',
4 => 'Ägile',
5 => 'Ãgile',
6 => 'Test',
);
如果我尝试使用 asort($array),我会得到以下结果:
Array
(
[0] => Agile
[6] => Test
[2] => Àgile
[1] => Ágile
[3] => Âgile
[5] => Ãgile
[4] => Ägile
)
并使用 natsort($array):
Array
(
[2] => Àgile
[1] => Ágile
[3] => Âgile
[5] => Ãgile
[4] => Ägile
[0] => Agile
[6] => Test
)
如何在 PHP 5 下实现返回正确结果顺序(0、1、2、3、4、5、6)的函数?所有多字节字符串函数(mbstring、iconv 等)都可以在我的系统上使用。
编辑:我想 natsort() 值,而不是键 - 我明确定义键(并使用 asort() 而不是 sort())的唯一原因是为了简化找出 unicode 值排序出错的地方的工作。
答:
natsort($array);
$array = array_values($array);
评论
这个问题并不像乍一看那么容易回答。这是PHP缺乏unicode支持的领域之一。
正如其他海报所建议的那样,natsort()
与要排序的类型的数组的排序无关。你要找的是一种语言环境感知排序机制,因为对带有扩展字符的字符串进行排序始终是所用语言的问题。让我们以德语为例:A 和 Ä 有时可以像是同一个字母一样排序 (DIN 5007/1),有时 Ä 可以按实际的“AE”排序 (DIN 5007/2)。相比之下,在瑞典语中,Ä 位于字母表的末尾。
如果你不使用 Windows,你很幸运,因为 PHP 提供了一些功能。结合使用 setlocale
()、usort
()、strcoll()
和适合您语言的正确 UTF-8 语言环境,您可以得到如下结果:
$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);
请注意,必须使用 UTF-8 语言环境变体才能对 UTF-8 字符串进行排序。我将上面示例中的语言环境重置为其原始值,因为使用 setlocale()
设置语言环境可能会在其他正在运行的 PHP 脚本中引入副作用 - 有关详细信息,请参阅 PHP 手册。
当您使用 Windows 机器时,目前没有解决这个问题的方法,而且我认为在 PHP 6 之前不会有任何解决方案。请参阅我自己关于针对此特定问题的 SO 的问题。
评论
搞定了!
$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);
输出:
Array
(
[0] => Agile
[1] => Ágile
[2] => Âgile
[3] => Àgile
[4] => Ãgile
[5] => Ägile
[6] => Test
[7] => かたかな
[8] => カタカナ
)
更好的是:
if (extension_loaded('intl') === true)
{
collator_asort(collator_create('root'), $array);
}
感谢@tchrist!
评论
我在这个问题上苦苦挣扎。
排序:
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
)
把 África 放在最后。我用这段肮脏的小代码解决了它(这适合我的目的和我的时间框架):
$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];
}
评论
preg_replace
array_multisort
我还有另一种解决方法,可以解决那些不起作用且未启用该模块的问题:setlocale
intl
// 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;
});
下一个:PHP 字符编码问题
评论