PHP中支持Unicode的自然排序算法?

PHP中支持Unicode的自然排序算法?,php,arrays,sorting,unicode,utf-8,Php,Arrays,Sorting,Unicode,Utf 8,是否可以使用自然顺序算法在PHP中对具有Unicode/UTF-8字符的数组进行排序?例如(此数组中的顺序正确排列): 如果我尝试使用asort($array),我会得到以下结果: Array ( [0] => Agile [6] => Test [2] => Àgile [1] => Ágile [3] => Âgile [5] => Ãgile [4] => Ägile ) 并使用natsor

是否可以使用自然顺序算法在PHP中对具有Unicode/UTF-8字符的数组进行排序?例如(此数组中的顺序正确排列):

如果我尝试使用asort($array),我会得到以下结果:

Array
(
    [0] => Agile
    [6] => Test
    [2] => Àgile
    [1] => Ágile
    [3] => Âgile
    [5] => Ãgile
    [4] => Ägile
)
并使用natsort($array):

如何在PHP5下实现返回正确结果顺序(0、1、2、3、4、5、6)的函数?我的系统上提供了所有多字节字符串函数(mbstring、iconv等)


编辑:我想对值进行natsort(),而不是键-我显式定义键(并使用asort()而不是sort())的唯一原因是为了简化查找unicode值排序错误的工作。

这个问题并不像第一眼看到的那么容易回答。这是PHP缺乏unicode支持的一个方面,它给您带来了巨大的冲击

natsort($array);
$array = array_values($array);
首先,正如其他海报所建议的那个样,它和要排序的类型的排序数组无关。您需要的是一种区域设置感知排序机制,因为使用扩展字符对字符串进行排序始终是一个使用语言的问题。让我们以德语为例:A和Ä有时可以像它们是同一个字母一样排序(DIN 5007/1),有时Ä可以像它实际上是“AE”(DIN 5007/2)一样排序。相反,在瑞典语中,Ä位于字母表的末尾

如果你不使用Windows,你很幸运,因为PHP提供了一些函数来实现这一点。结合使用,并为您的语言使用正确的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);
$array=array('chile'、'chile'、'chile'、'chile'、'chile'、'chile'、'Agile'、'Test');
$oldLocal=setlocale(LC_COLLATE,'.utf8');
usort($array,'strcoll');
setlocale(LC_COLLATE,$oldLocal);
请注意,为了对UTF-8字符串进行排序,必须使用UTF-8语言环境变量。我将上面示例中的区域设置重置为其原始值,因为使用设置区域设置会在其他正在运行的PHP脚本中引入副作用-有关更多详细信息,请参阅PHP手册


当您使用Windows机器时,目前没有解决此问题的方法,我认为在PHP 6之前不会有任何方法。请看我自己的,所以针对这个具体问题。

解决了它

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

更好:

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

我为这个问题与asort进行了斗争

分类:

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
)
把非洲放在最后。我用这段脏兮兮的代码解决了这个问题(它适合我的目的和时间框架):


对于那些
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;
});
//要排序的数组
$countries=数组(
'AT'=>Österreich,
“德”=>德国,
“CH”=>Schweiz,
);
//根据您的需要扩展此阵列。
$utf\u排序\u映射=数组(
“ä”=>“a”,
“Ä”=>“A”,
“Å”=>“A”,
“ö”=>“o”,
“Ö”=>“O”,
“u”=>“u”,
“Ü”=>“U”,
);
uasort($my\u数组,函数($a,$b)use($utf\u sort\u映射){
$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\U sort\u map[$initial\u a])){
$initial_a=$utf_sort_map[$initial_a];
}
if(isset($utf\U sort\u map[$initial\u b])){
$initial_b=$utf_sort_map[$initial_b];
}
如果($initial_a==$initial_b){
返回mb_substr($a,1)
我的示例中的键不是问题所在,它们只是用来帮助对unicode值进行排序。非常好的洞察力,我正在Windows上开发,但这将在*nix机器上运行。如果我没有弄错的话,PHP5.3将支持这种排序,尽管是某种类型的类,但是我不想依赖set_locale(),原因主要有两个:1)它不可预测(取决于操作系统可用的区域设置)2)它不是线程安全的,可能会在服务器上导致意外行为。使用ord()函数的多字节版本进行排序会得到与简单排序()完全相同的结果=(关于你的第一个评论:你完全正确,我的回答中提出的解决方案不是你所期望的,因为它既不可移植,也没有副作用。但是:它是目前唯一的一个——除了使用ext/mbstring等在字符和字节级别实现自己的排序之外。关于我的第二个评论,我使用d mbstring扩展名来编写一个多字节等效于原始PHP ord()函数的代码,但它给出的结果与sort()相同函数。是的,在MySQL服务器上对数据进行排序是一项可行的工作。MySQL不受这些限制。您可以通过为数据选择正确的拼贴来控制排序顺序。听起来您真正需要的是Unicode排序算法(UCA)。我有一个Perl演示,其中我提供了一个shell可调用版本,供那些可能没有合适的库可调用的人使用。也许这在这里也会有所帮助。@tchrist:UCA是我要找的,我稍后会仔细看看你的答案,谢谢提醒!;)你是法国人吗?您可能想查看我对这个问题的回答,我的
preg\u replace
方法的音译效果更好,
array\u multisort
函数还保留了值和非数字键的关联。
// 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;
});