一、一个具体的例子引发的问题

当今是国际化的时代,多种语言可能同时显示在屏幕上。比如一个人可能喜欢听华语歌、英文歌、韩文歌和日语歌,又比如他的联系人中有中国人、英国人、日本人、韩国人以及有英文名字的中国人。
在这种情况下,他的手机上需要维护一个列表,每一项可能是中文、韩文、英文和日文。在许多情况下需要对这个列表进行维护,那么如何对这些表项进行排序呢?为了解决这个问题,至少要回答以下几个问题:

  1. 中文、韩文、英文、日文的顺序是怎样的呢?那个语种在前面呢?
  2. 同一个语种内部如何排序呢?比如中文,是按照汉语拼音还是部首,抑或是笔画数排序呢?
  3. 对于数字、标点符号、特殊符号等独立于某一个书写系统的字符 (character),如何处理呢?

二、事实上的排序标准: UCA+CLDR

使用这种方法来排序的公司和组织有 Apple、Google、IBM、MicroSoft、Amazon、Python、Wikimedia Foundation (Wikipedia)、Apache...
在苹果的文档中,可以找到下面的描述:

Localized string comparisons are based on the Unicode Collation Algorithm, as tailored for different languages by CLDR (Common Locale Data Repository).

  1. UCA
    首先解释一下 Collation 的含义。

    Collation is the general term for the process and function of determining the sorting order of strings of characters.

    如果有若干字符串需要排序,确定排序的过程就是 Collation,可以认为是排序 (sort) 在字符串领域的特化。
    Unicode Collation Algorithm (UCA) 是 Unicode 制定的如何比较两个 Unicode 字符串的规范。注意这里是字符串,而不仅仅是字符。
    UCA 的规则很复杂,我们以后再说。但从名字上可以看出,UCA 只是一个算法,算法需要数据才能产出结果。
    UCA 最后产出了一个文档,指定了默认情况下 Unicode 字符的顺序。但是这仅仅是默认情况,也就是照顾了大多数情况(也就是排序对英语国家比较友好。。。)。对于其他地区的人们来说,就需要输入和默认情况不同的数据,以获得和当地习惯相符合的结果。比如同样的汉字,在中国大陆是按照汉语拼音排序的,在香港就是按照笔画数目排序的。

  2. CLDR
    Common Locale Data Repository (CLDR),从名字上可以看出,这个实际上是一堆数据的仓库。对于指定的地区 (locale),可以从中找到指定的数据。再结合 UCA,就可以得到符合当期习惯的排序结果。

三、UCA 默认顺序

UCA 最后产出了一个文档,在common/uca/allkeys_CLDR.txt。这个文档就指定了默认情况下的 Unicode 字符的顺序。

    0031  ; [.1B3F.0020.0002] # DIGIT ONE
0661 ; [.1B3F.0020.0002] # ARABIC-INDIC DIGIT ONE

这个文档中的每一行都有上面的格式。分号;之前的部分是 Unicode 字符对应的 Unicode 码点 (code point)。分号之后是用于 UCA 算法的权重,用.分隔。#及之后部分是注释。
所有的 Unicode 字符从上到下依次排序。

3.1 Unicode 字符分类

Unicode 把所有字符分为两类,并按顺序排列:

  1. common characters
    包括空格、标点、通用符号、货币符号和数字。
  2. script characters
    包括拉丁字母、希腊字母、汉字等。

把字符分类,便于把某一类字符统统放到另一类字符之前,比如把汉字放在英文之前。
注意:这里默认排序并不是按照 Unicode 码点顺序依次排列!

3.2 利用 UCA 默认值排序的例子

rawArray = @[@"cc",@"曹操",@"bb",@"1",@"١",@"(en",@"(zh"];
NSArray *sortedArray = [rawArray sortedArrayUsingComparator:^NSComparisonResult(NSString * _Nonnull obj1, NSString * _Nonnull obj2) {
return [obj1 compare:obj2 options:NSCaseInsensitiveSearch];
}];
__block NSMutableArray *codeUnits = [NSMutableArray array];
[sortedArray enumerateObjectsUsingBlock:^(NSString* _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
[codeUnits addObject:@([obj characterAtIndex:0])];
}];
NSLog(@"after default sort , result is %@, codeUnits is %@",sortedArray,codeUnits); NSLocale *locale=[[NSLocale alloc] initWithLocaleIdentifier:@"en"];
sortedArray = [rawArray sortedArrayUsingComparator:^NSComparisonResult(NSString * _Nonnull obj1, NSString * _Nonnull obj2) {
NSRange string1Range = NSMakeRange(0, [obj1 length]);
return [obj1 compare:obj2 options:0 range:string1Range locale:locale];
}];
NSLog(@"after locale sort , result is %@",sortedArray);

输出结果如下:

after default sort , result is ((en,1,bb,cc,١,曹操,(zh,), codeUnits is (40,49,98,99,1633,26361,65288,)
after locale sort , result is ((en,(zh,1,١,bb,cc,曹操,)

未指定地区信息时,NSString的排序函数仅仅是按照UTF-16 code unit 的值排列的。在指定地区信息是en后,按照allkeys_CLDR.txt中指定的顺序,也更加符合人们的预期。依次是标点符号(英文和中文的左括号)、数字(阿拉伯数字 1 和另一种表示方法)、scripts(英文和中文)

四、CLDR 对排序结果进行调整 (tailor)

如上图,CLDR 最新版本的数据有很多文件夹,都是用一种标记语言 (LDML) 来书写的。我们一步一步来确定如何决定在中国大陆对 Unicode 字符进行排序。

4.1 确定采用何种排序规则

bcp47/collation.xml中,指出了可选的很多种排序方式,包括standardpinyinbig5hanstroke(笔画排序)。

那么在中国大陆应该采用哪种排序方法呢?可以在collation/zh.xml中找到指定的排序方式是pinyin。而繁体中文的默认排序方式是stroke

    <defaultCollation>pinyin</defaultCollation> in  collation/zh.xml
<defaultCollation>stroke</defaultCollation> in collation/zh_Hant.xml

4.2 确定汉语内部的排序规则

collation/zh.xml 中可以看到下图。和程序中的头文件一样,pinyin排序规则引入了private-pinyin这个规则,此外,还引入了默认的规则。即如果两个字符(如阿拉伯数字 1 和 2 )在pinyin规则中找不到依据,那么根据默认规则进行排序。这样子做大大降低了维护成本,在原理上和 "Don't repeat yourself" 类似。
我们现在来看具体的排序规则。

  1. private-pinyin中,指定了可以标“声调”的字母在各种声调情况下的排列顺序。
    你没有看错,mn也可以标声调!
  2. 指定了拼音的顺序以及同音字的顺序
    首先按照拼音排序,表现为不同行之间的顺序。对于同音字,也就是每一行之间的顺序,先按照笔画数排序,再按照kRSUnicode排序。
    请注意,在ā之前有一行,是用来构建索引的,即此行之下直至另一个索引都属于A的索引之内。

4.3 不同语种字母的顺序

当指定了地区之后,这个地区的字符将会在所有的 “script characters” 中排列第一。其他地区的字符按照默认顺序排序。

  1. 确定当地的书写系统 (scripts)
    ./main/zh_Hans_CN.xml中,看到<script type="Hans"/>
  2. 确定书写系统包括哪些字符
    ./uca/FractionalUCA.txt中,搜索first primary,得到下图:

    在此行之下,即是所有汉字。第一个就是康熙字典的“一”,第二个是“㊀”。
  3. 其他书写系统的字符的顺序
    依据默认的顺序,即在common/uca/allkeys_CLDR.txt规定的顺序。

4.4 一个例子

对于以下的字符串数组排序:

    rawArray = @[@"上",@"㊤",@"μ",@"язык"];

这个字符数组中,包括汉语、希腊语和俄语。指定不同的地区之后,得到不同的结果。

locale is el_CN, result is (µ,язык,上,㊤,)  //希腊语
locale is ru_CN, result is (язык,µ,上,㊤,) //俄语
locale is zh_CN, result is (上,㊤,µ,язык,) //中国
locale is en, result is (µ,язык,上,㊤,) //英语

五、参考资料

  1. UNICODE COLLATION ALGORITHM
  2. CLDR
  3. CLDR 30.0.3
  4. UNICODE LOCALE DATA MARKUP LANGUAGE
  5. UNICODE HAN DATABASE (UNIHAN)

Unicode 字符串排序规则(一):如何确定单个字符的顺序的更多相关文章

  1. Unicode 字符串排序规则(二):如何比较字符串

    一.UCA 简介 Unicode Collation Algorithm (UCA) 是 Unicode 规定的如何比较两个字符串大小的算法,也是事实上的标准.我们先来看下它的几个特征. 1.1 Mu ...

  2. Python2 处理 Unicode 字符串的规则

    在 Python2 中处理 Unicode 字符串,需遵循如下规则: 1. 程序中的字符串要加前缀 u 2. 不要用 str(),而应该用 unicode() 作为字符串转换函数.不要使用 chr() ...

  3. MS SQL 排序规则总结

    排序规则术语        什么是排序规则呢? 排序规则是根据特定语言和区域设置标准指定对字符串数据进行排序和比较的规则.SQL Server 支持在单个数据库中存储具有不同排序规则的对象.MSDN解 ...

  4. sqlserver之排序规则和ETL不支持sqlserverdatetime2的问题

    sqlserver的排序规则大概分为Windows 排序规则和 SQL Server 排序规则.数据在安装的时候,默认不设置会默认为SQL_Latin1_General_CP1_CI_AI.数据库在创 ...

  5. SQL 排序规则问题

    http://blog.csdn.net/delphigbg/article/details/12744807 MSSQL排序规则总结   什么是排序规则呢? 排序规则根据特定语言和区域设置标准指定对 ...

  6. sql server 排序规则

    /*   排序规则根据特定语言和区域设置的标准指定对  字符串  数据 进行排序和比较的规则.   以 ORDER BY 子句为例:如果按升序排列,说英语的人认为字符串 Chiapas 应排在 Col ...

  7. SQL Server更改排序规则的实现过程

    摘自: http://www.2cto.com/database/201112/115138.html 以下的文章主要向大家描述的是SQL Server更改排序规则的实现过程,以及在实现其实际操作过程 ...

  8. 数据库排序规则的冲突(理解collate Chinese_PRC_CI_AS)

    之前碰到了数据库排序规则冲突问题,即百度或者 Google 的老话题: “ 无法解决 equal to 操作中‘ sql_latin1_general_cp1_ci_as ’和‘ chinese_pr ...

  9. [转]LocalDB数据库修改排序规则,修复汉字变问号

    VS中新增的轻量级数据库LocalDB,有个这个,开发人员就不必再安装庞大的SQL server了,可以方便地测试运行小型项目:既然是轻量级数据库,它抛弃了庞大的身躯,功能上当然也会受到局限,其中之一 ...

随机推荐

  1. linux下用python搭建简单的httpServer

    1.服务器端:python -m SimpleHTTPServer 12000 python -m :  相当于import,当做模块来启动; 后面的12000代表的是端口 使用浏览器打开如下: 2. ...

  2. R语言-散点图阵

    1.pairs()函数 > pairs(iris[,1:4]) #取iris数据集的第一列到第四列两两作图 2.plot()函数 > plot(iris[,1:4], + main=&qu ...

  3. mysql,utf8,utf8mb4

    参考文章 https://www.cnblogs.com/beyang/p/7580814.html https://blog.csdn.net/testcs_dn/article/details/7 ...

  4. oracle随机数(转)

    1.从表中随机取记录SELECT * FROM (SELECT * FROM STUDENT ORDER BY DBMS_RANDOM.RANDOM) WHERE ROWNUM < 4--表示从 ...

  5. nginx命令启动及选项

    [root@ke]# nginx -h  #this help [root@ke]# nginx -t  #检查配置文件的语法 [root@ke]# nginx -T  #检查配置文件的语法并输出 [ ...

  6. Linux系统性能监控工具:tsar 安装、配置、以及使用

    介绍 tsar 是淘宝自己开发的一个监控工具,可用于收集和汇总系统信息,例如CPU,负载,IO和应用程序信息,例如nginx,HAProxy,Squid等.结果可以存储在本地磁盘或发送到Nagios. ...

  7. POJ 3107.Godfather 树形dp

    Godfather Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 7536   Accepted: 2659 Descrip ...

  8. python 的面相对象编程--对应c++

    在python的面相对象编程中,我们常常在class中可以看到a(),  _b() ,  __c(), __d()__这样的函数. 由于我是看廖雪峰老师的教程,廖老师为了简单起见,没有引入太多概念,我 ...

  9. Object.defineProperty之observe实现

    对数据对象的属性批量劫持设置: <script type="text/javascript"> function observe(data){ if(!data || ...

  10. 源码管理工具Git-客户端GitBash常用命令

    1.配置用户名和邮箱地址(第一次启动程序时配置,以后使用不用配置)git config --global user.name "dolen"git config --global ...