一 Unicode 字符串排序规则:如何确定单个字符的顺序

一、一个具体的例子引发的问题
当今是国际化的时代 , 多种语言可能同时显示在屏幕上 。比如一个人可能喜欢听华语歌、英文歌、韩文歌和日语歌 , 又比如他的联系人中有中国人、英国人、日本人、韩国人以及有英文名字的中国人 。
在这种情况下 , 他的手机上需要维护一个列表 , 每一项可能是中文、韩文、英文和日文 。在许多情况下需要对这个列表进行维护 , 那么如何对这些表项进行排序呢?为了解决这个问题 , 至少要回答以下几个问题:
中文、韩文、英文、日文的顺序是怎样的呢?那个语种在前面呢?同一个语种内部如何排序呢?比如中文 , 是按照汉语拼音还是部首 , 抑或是笔画数排序呢?对于数字、标点符号、特殊符号等独立于某一个书写系统的字符 () , 如何处理呢? 二、事实上的排序标准: UCA+CLDR
使用这种方法来排序的公司和组织有 Apple、、IBM、、、、()、...
在苹果的文档中 , 可以找到下面的描述:
are based on the, asforby CLDR (Data ).
UCA
首先解释一下的含义 。
is theterm for theandoftheorder ofof .
如果有若干字符串需要排序 , 确定排序的过程就是  , 可以认为是排序 (sort) 在字符串领域的特化 。
(UCA) 是制定的如何比较两个字符串的规范 。注意这里是字符串 , 而不仅仅是字符 。
UCA 的规则很复杂 , 我们以后再说 。但从名字上可以看出 , UCA 只是一个算法 , 算法需要数据才能产出结果 。
UCA 最后产出了一个文档 , 指定了默认情况下字符的顺序 。但是这仅仅是默认情况 , 也就是照顾了大多数情况(也就是排序对英语国家比较友好 。。。) 。对于其他地区的人们来说 , 就需要输入和默认情况不同的数据 , 以获得和当地习惯相符合的结果 。比如同样的汉字 , 在中国大陆是按照汉语拼音排序的 , 在香港就是按照笔画数目排序的 。
CLDR
Data(CLDR) , 从名字上可以看出 , 这个实际上是一堆数据的仓库 。对于指定的地区 () , 可以从中找到指定的数据 。再结合 UCA , 就可以得到符合当期习惯的排序结果 。
三、UCA 默认顺序
UCA 最后产出了一个文档 , 在/uca/.txt 。这个文档就指定了默认情况下的字符的顺序 。
0031; [.1B3F.0020.0002] # DIGIT ONE0661; [.1B3F.0020.0002] # ARABIC-INDIC DIGIT ONE
这个文档中的每一行都有上面的格式 。分号;之前的部分是字符对应的码点 (code point) 。分号之后是用于 UCA 算法的权重 , 用.分隔 。#及之后部分是注释 。
所有的字符从上到下依次排序 。
3.1字符分类
把所有字符分为两类 , 并按顺序排列:
包括空格、标点、通用符号、货币符号和数字 。
包括拉丁字母、希腊字母、汉字等 。
把字符分类 , 便于把某一类字符统统放到另一类字符之前 , 比如把汉字放在英文之前 。
注意:这里默认排序并不是按照码点顺序依次排列!
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,曹操,)
未指定地区信息时 , 的排序函数仅仅是按照UTF-16 code unit 的值排列的 。在指定地区信息是en后 , 按照.txt中指定的顺序 , 也更加符合人们的预期 。依次是标点符号(英文和中文的左括号)、数字(阿拉伯数字 1 和另一种表示方法)、(英文和中文)
四、CLDR 对排序结果进行调整 ()

一  Unicode 字符串排序规则:如何确定单个字符的顺序

文章插图
如上图 , CLDR 最新版本的数据有很多文件夹 , 都是用一种标记语言 (LDML) 来书写的 。我们一步一步来确定如何决定在中国大陆对字符进行排序 。
【一Unicode 字符串排序规则:如何确定单个字符的顺序】4.1 确定采用何种排序规则
在bcp47/.xml中 , 指出了可选的很多种排序方式 , 包括、、、(笔画排序) 。
那么在中国大陆应该采用哪种排序方法呢?可以在/zh.xml中找到指定的排序方式是 。而繁体中文的默认排序方式是 。
pinyin incollation/zh.xmlstroke in collation/zh_Hant.xml
4.2 确定汉语内部的排序规则
在/zh.xml 中可以看到下图 。和程序中的头文件一样 , 排序规则引入了-这个规则 , 此外 , 还引入了默认的规则 。即如果两个字符(如阿拉伯数字 1 和 2 )在规则中找不到依据 , 那么根据默认规则进行排序 。这样子做大大降低了维护成本 , 在原理上和 "Don't" 类似 。
我们现在来看具体的排序规则 。
在-中 , 指定了可以标“声调”的字母在各种声调情况下的排列顺序 。
你没有看错 , m和n也可以标声调!指定了拼音的顺序以及同音字的顺序
首先按照拼音排序 , 表现为不同行之间的顺序 。对于同音字 , 也就是每一行之间的顺序 , 先按照笔画数排序 , 再按照排序 。
请注意 , 在ā之前有一行 , 是用来构建索引的 , 即此行之下直至另一个索引都属于A的索引之内 。4.3 不同语种字母的顺序
当指定了地区之后 , 这个地区的字符将会在所有的 “ ” 中排列第一 。其他地区的字符按照默认顺序排序 。
确定当地的书写系统 ()
在./main/.xml中 , 看到