Unicode 字符串排序规则(一):如何确定单个字符的顺序
一、一个具体的例子引发的问题
当今是国际化的时代,多种语言可能同时显示在屏幕上。比如一个人可能喜欢听华语歌、英文歌、韩文歌和日语歌,又比如他的联系人中有中国人、英国人、日本人、韩国人以及有英文名字的中国人。
在这种情况下,他的手机上需要维护一个列表,每一项可能是中文、韩文、英文和日文。在许多情况下需要对这个列表进行维护,那么如何对这些表项进行排序呢?为了解决这个问题,至少要回答以下几个问题:
中文、韩文、英文、日文的顺序是怎样的呢?那个语种在前面呢?同一个语种内部如何排序呢?比如中文,是按照汉语拼音还是部首,抑或是笔画数排序呢?对于数字、标点符号、特殊符号等独立于某一个书写系统的字符 (),如何处理呢? 二、事实上的排序标准: UCA+CLDR
使用这种方法来排序的公司和组织有 Apple、、IBM、、、、 ()、...
在苹果的文档中,可以找到下面的描述:
are based on the , as for by CLDR ( Data ).
UCA
首先解释一下 的含义。
is the term for the and of the order of of .
如果有若干字符串需要排序,确定排序的过程就是 ,可以认为是排序 (sort) 在字符串领域的特化。
(UCA) 是 制定的如何比较两个 字符串的规范。注意这里是字符串,而不仅仅是字符。
UCA 的规则很复杂,我们以后再说。但从名字上可以看出,UCA 只是一个算法,算法需要数据才能产出结果。
UCA 最后产出了一个文档,指定了默认情况下 字符的顺序。但是这仅仅是默认情况,也就是照顾了大多数情况(也就是排序对英语国家比较友好。。。)。对于其他地区的人们来说,就需要输入和默认情况不同的数据,以获得和当地习惯相符合的结果。比如同样的汉字,在中国大陆是按照汉语拼音排序的,在香港就是按照笔画数目排序的。
CLDR
Data (CLDR),从名字上可以看出,这个实际上是一堆数据的仓库。对于指定的地区 (),可以从中找到指定的数据。再结合 UCA,就可以得到符合当期习惯的排序结果。
三、UCA 默认顺序
UCA 最后产出了一个文档,在/uca/.txt。这个文档就指定了默认情况下的 字符的顺序。
0031 ; [.1B3F.0020.0002] # DIGIT ONE 0661 ; [.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 对排序结果进行调整 ()
如上图,CLDR 最新版本的数据有很多文件夹,都是用一种标记语言 (LDML) 来书写的。我们一步一步来确定如何决定在中国大陆对 字符进行排序。
4.1 确定采用何种排序规则
在bcp47/.xml中,指出了可选的很多种排序方式,包括、、、(笔画排序)。
那么在中国大陆应该采用哪种排序方法呢?可以在/zh.xml中找到指定的排序方式是。而繁体中文的默认排序方式是。
pinyin in collation/zh.xml stroke in collation/zh_Hant.xml
4.2 确定汉语内部的排序规则
在/zh.xml 中可以看到下图。和程序中的头文件一样,排序规则引入了-这个规则,此外,还引入了默认的规则。即如果两个字符(如阿拉伯数字 1 和 2 )在规则中找不到依据,那么根据默认规则进行排序。这样子做大大降低了维护成本,在原理上和 "Don't " 类似。
我们现在来看具体的排序规则。
在-中,指定了可以标“声调”的字母在各种声调情况下的排列顺序。
你没有看错,m和n也可以标声调!指定了拼音的顺序以及同音字的顺序
首先按照拼音排序,表现为不同行之间的顺序。对于同音字,也就是每一行之间的顺序,先按照笔画数排序,再按照排序。
请注意,在ā之前有一行,是用来构建索引的,即此行之下直至另一个索引都属于A的索引之内。 4.3 不同语种字母的顺序
当指定了地区之后,这个地区的字符将会在所有的 “ ” 中排列第一。其他地区的字符按照默认顺序排序。
确定当地的书写系统 ()
在./main/.xml中,看到