这几天在搞一个网站,里面涉及到对一些数据进行排序显示的问题。后来做完之后自己用着发现因为默认是按英文排序的,当语言是中文的时候要找一个项特别麻烦。那么问题就来了,中文要怎么样才能按照用户习惯的顺序来排序呢(中国大陆来说,就是拼音排序)?

经过一番百度Google,找到的资料要么就是转成GB2312然后排序,要么就是查表之类的,费劲又不安全(转码的时候可能会有意外字符导致失败之类的,而且GB2312并不完全是按拼音排序的;查表的话数据的正确性难以有保障)。作为一个对代码正确性要求很高的人,这显然不符合我的风格。

然后我突然想到了万能的UnicodeUnicode并不只是单纯的一个编码系统,而是有关语言文字的很多内容的一整套完整的编码、属性体系。我这个项目里面用到的翻译部分就是使用了PHP的intl扩展,而这个扩展是基于ICU这个项目实现的,而ICU又是基于Unicode组织提供的CLDR数据开发的一套完整的国际化解决方案。CLDR,全称叫做Unicode Common Locale Data Repository,是Unicode组织提供的一套基本的语言相关的数据,包含了日期时间货币数字格式等等东西,也包含了排序规则,这就是本文要用到的东西。


废话讲完了,开始说正题。下面以三个国家名为例。

  • 新西兰
  • 贝宁
  • 澳大利亚

选这三个国家的原因是,PHP默认排序(sort)、拼音排序以及笔画排序应用到这三个国家上面的结果都不一样,方便查看效果。代码如下:

<?php
$data = ['新西兰', '贝宁', '澳大利亚'];

sort($data); // 默认排序(即直接按照二进制排序)
var_dump($data);

$coll = collator_create('zh-CN'); // 使用中国大陆的语言习惯(拼音排序)
usort($data, [$coll, 'compare']);
var_dump($data);

$coll = collator_create('zh-TW'); // 使用台湾的语言习惯(笔画排序)
usort($data, [$coll, 'compare']);
var_dump($data);

在上面代码中根据语言习惯排序的时候使用了Collator::compare作为自定义比较函数,这样通过指定不同的语言,就能得到不同的排序结果。上面代码的结果如下:

array(3) {
  [0]=>
  string(9) "新西兰"
  [1]=>
  string(12) "澳大利亚"
  [2]=>
  string(6) "贝宁"
}
array(3) {
  [0]=>
  string(12) "澳大利亚"
  [1]=>
  string(6) "贝宁"
  [2]=>
  string(9) "新西兰"
}
array(3) {
  [0]=>
  string(6) "贝宁"
  [1]=>
  string(9) "新西兰"
  [2]=>
  string(12) "澳大利亚"
}

需要注意的是,这个Collator类里面还提供了一个sort函数和一个sortWithSortKeys函数,sort函数的效果似乎和PHP语言提供的sort效果完全一样,而sortWithSortKeys的效果完全不知所云。。另外,以上代码需要安装PHP的intl扩展才能正确运行,而且intl扩展还依赖icu库,所以最好先装最新的icu库然后再装intl扩展。

另外,可能也有需要在语言是简体中文(即Locale是zh-CN)的时候按照笔画数排序,那么这时候可以在Locale后面加参数,比如zh-CN-u-co-stroke或者zh-CN@collation=stroke的效果就是笔画排序,当然还有一些其他参数可选,可以参考这里