Unicode 字符串排序规则(一):如何确定单个字符的顺序
一、一个具体的例子引发的问题
当今是国际化的时代,多种语言可能同时显示在屏幕上。比如一个人可能喜欢听华语歌、英文歌、韩文歌和日语歌,又比如他的联系人中有中国人、英国人、日本人、韩国人以及有英文名字的中国人。
在这种情况下,他的手机上需要维护一个列表,每一项可能是中文、韩文、英文和日文。在许多情况下需要对这个列表进行维护,那么如何对这些表项进行排序呢?为了解决这个问题,至少要回答以下几个问题:
- 中文、韩文、英文、日文的顺序是怎样的呢?那个语种在前面呢?
- 同一个语种内部如何排序呢?比如中文,是按照汉语拼音还是部首,抑或是笔画数排序呢?
- 对于数字、标点符号、特殊符号等独立于某一个书写系统的字符 (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).
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 字符的顺序。但是这仅仅是默认情况,也就是照顾了大多数情况(也就是排序对英语国家比较友好。。。)。对于其他地区的人们来说,就需要输入和默认情况不同的数据,以获得和当地习惯相符合的结果。比如同样的汉字,在中国大陆是按照汉语拼音排序的,在香港就是按照笔画数目排序的。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 把所有字符分为两类,并按顺序排列:
- common characters
包括空格、标点、通用符号、货币符号和数字。 - 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中,指出了可选的很多种排序方式,包括standard、pinyin、big5han、stroke(笔画排序)。

那么在中国大陆应该采用哪种排序方法呢?可以在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" 类似。 
我们现在来看具体的排序规则。
- 在
private-pinyin中,指定了可以标“声调”的字母在各种声调情况下的排列顺序。
你没有看错,m和n也可以标声调! - 指定了拼音的顺序以及同音字的顺序
首先按照拼音排序,表现为不同行之间的顺序。对于同音字,也就是每一行之间的顺序,先按照笔画数排序,再按照kRSUnicode排序。
请注意,在ā之前有一行,是用来构建索引的,即此行之下直至另一个索引都属于A的索引之内。
4.3 不同语种字母的顺序
当指定了地区之后,这个地区的字符将会在所有的 “script characters” 中排列第一。其他地区的字符按照默认顺序排序。
- 确定当地的书写系统 (scripts)
在./main/zh_Hans_CN.xml中,看到<script type="Hans"/> - 确定书写系统包括哪些字符
在./uca/FractionalUCA.txt中,搜索first primary,得到下图:

在此行之下,即是所有汉字。第一个就是康熙字典的“一”,第二个是“㊀”。 - 其他书写系统的字符的顺序
依据默认的顺序,即在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 (µ,язык,上,㊤,) //英语
五、参考资料
- UNICODE COLLATION ALGORITHM
- CLDR
- CLDR 30.0.3
- UNICODE LOCALE DATA MARKUP LANGUAGE
- UNICODE HAN DATABASE (UNIHAN)
Unicode 字符串排序规则(一):如何确定单个字符的顺序的更多相关文章
- Unicode 字符串排序规则(二):如何比较字符串
一.UCA 简介 Unicode Collation Algorithm (UCA) 是 Unicode 规定的如何比较两个字符串大小的算法,也是事实上的标准.我们先来看下它的几个特征. 1.1 Mu ...
- Python2 处理 Unicode 字符串的规则
在 Python2 中处理 Unicode 字符串,需遵循如下规则: 1. 程序中的字符串要加前缀 u 2. 不要用 str(),而应该用 unicode() 作为字符串转换函数.不要使用 chr() ...
- MS SQL 排序规则总结
排序规则术语 什么是排序规则呢? 排序规则是根据特定语言和区域设置标准指定对字符串数据进行排序和比较的规则.SQL Server 支持在单个数据库中存储具有不同排序规则的对象.MSDN解 ...
- sqlserver之排序规则和ETL不支持sqlserverdatetime2的问题
sqlserver的排序规则大概分为Windows 排序规则和 SQL Server 排序规则.数据在安装的时候,默认不设置会默认为SQL_Latin1_General_CP1_CI_AI.数据库在创 ...
- SQL 排序规则问题
http://blog.csdn.net/delphigbg/article/details/12744807 MSSQL排序规则总结 什么是排序规则呢? 排序规则根据特定语言和区域设置标准指定对 ...
- sql server 排序规则
/* 排序规则根据特定语言和区域设置的标准指定对 字符串 数据 进行排序和比较的规则. 以 ORDER BY 子句为例:如果按升序排列,说英语的人认为字符串 Chiapas 应排在 Col ...
- SQL Server更改排序规则的实现过程
摘自: http://www.2cto.com/database/201112/115138.html 以下的文章主要向大家描述的是SQL Server更改排序规则的实现过程,以及在实现其实际操作过程 ...
- 数据库排序规则的冲突(理解collate Chinese_PRC_CI_AS)
之前碰到了数据库排序规则冲突问题,即百度或者 Google 的老话题: “ 无法解决 equal to 操作中‘ sql_latin1_general_cp1_ci_as ’和‘ chinese_pr ...
- [转]LocalDB数据库修改排序规则,修复汉字变问号
VS中新增的轻量级数据库LocalDB,有个这个,开发人员就不必再安装庞大的SQL server了,可以方便地测试运行小型项目:既然是轻量级数据库,它抛弃了庞大的身躯,功能上当然也会受到局限,其中之一 ...
随机推荐
- Ubuntu下解决MySQL自启动,chkconfig list 全部off 情况
chkconfig命令是用于RedHat/Fedora发行版的,而对于像Ubuntu之类的Debian发行版,应该使用这个命令: sudo update-rc.d mysql defaults 验证一 ...
- 微探eventlet.monkey_patch
e ventlet.monkey_patch在运行时动态修改已有的代码,而不需要修改原始代码 在eventlet.monkey_patch中支持以下几种python原生库修改 eventlet.mon ...
- 【笔记】计算机原理,网络,Linux操作系统
一,计算机原理 1,化繁为简的思想,产生二进制,产生把所有计算归结为加法运算 二,网络 1,分层思想,产生ISO七层协议,商用了TCP/IP的五层协议 三,Linux操作系统 1,分层思想,硬件-&g ...
- Appium 学习二:切换Webview
由于测试的APP是混合应用,即包含了原生代码和web网页. 混合应用在应用程序中嵌入了Webview,Webview是用来访问网页的一个控件.Webview内核也分为原生和第三方(比如腾讯X5内核) ...
- 获取CNVD的cookie
def getCookie(self): #获取cookie spiderFirefox = webdriver.Firefox() spiderFirefox.get("http://ww ...
- Python开发——函数【基础】
函数的定义 以下规则 函数代码块以 def 关键词开头,后接函数标识符名称和圆括号(). 任何传入参数和自变量必须放在圆括号中间.圆括号之间可以用于定义参数. 函数的第一行语句可以选择性地使用文档字符 ...
- eclipse-查看继承层次图/继承实现层次图
阅读代码时,如果想要看某个类继承了哪些类.实现了哪些接口.哪些类继承了这个类,恰巧这个类的继承实现结构又比较复杂,那么如果对开发工具不是很熟练,这个需求是比较难以实现的.eclipse中的type h ...
- dtruss
一.简介 系统调用跟踪工具. 二.实例
- setsockopt设置socket状态
setsockopt设置socket状态 1.closesocket(一般不会立即关闭而经历TIME_WAIT的过程)后想继续重用该socket:BOOL bReuseaddr=TRUE;setsoc ...
- java多线程系列16 线程池
当系统系统规模较小,我们可以不使用线程池.但是当系统到达一定规模,频繁的创建和销毁线程池会消耗很多资源. 合理利用线程池能够带来三个好处. 1降低资源消耗.通过重复利用已创建的线程降低线程创建和销毁造 ...