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

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

  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. nodejs 箭头函数

    背景 箭头函数,出现于ES6规范中. 使用 就是lambda函数. 一般使用: (a, b) => { return a + b; } 简略模式: 当参数只有一个时,可以省略括号:当返回值只有一 ...

  2. Cannot resolve method

    1.问题描述: 本人idea下拉项目,结果impl里的类满屏的红色 2.解决方法 步骤: File --> Settings --> Plugins -->搜索并安装lom即可

  3. vba文件对比并高亮显示

    每月月底要和人事要离职人员名单,并账号列表里删除已经离职人员的账号,如下代码通过将账号列表与人事发来的离职清单进行对比,高亮找出离职人员的账号,并进行删除. Sub DeleteMain() Dim ...

  4. (转载)new Thread的弊端及Java四种线程池的使用

    介绍new Thread的弊端及Java四种线程池的使用,对Android同样适用.本文是基础篇,后面会分享下线程池一些高级功能. 1.new Thread的弊端 执行一个异步任务你还只是如下new ...

  5. Linux用户态驱动设计

    聊聊Linux用户态驱动设计   序言 设备驱动可以运行在内核态,也可以运行在用户态,用户态驱动的利弊网上有很多的讨论,而且有些还上升到政治性上,这里不再多做讨论.不管用户态驱动还是内核态驱动,他们都 ...

  6. mysql找到数据的存储位置

    本来是想找mysql数据库文件中的sql脚本文件的,结果发现运行了sql脚本文件后,你删除了,就没有sql语句了,那么我们分析一下在数据库路径下面找到的文件又是什么呢? 1.先找mysql中data的 ...

  7. Effective C++ 笔记:条款 30 inline

    30 : Understand the ins and outs of inlining 1 inline申请书 1.1 类内部实现函数包含隐藏的inline申请 class Human { publ ...

  8. LOJ-10106(有向图欧拉回路的判断)

    题目链接:传送门 思路: (1)将每个单词视为有向路径,单词的起始字母是起始节点,末尾字母是终止节点,然后找由字母建立的有向图 是否是欧拉图或者半欧拉图. (2)先用并查集判断是否连通,再判断入度与出 ...

  9. 部署eclipse项目到tomcat

    1.为了以防万一,将本地tomcat版本及其jdk版本与服务器上的版本最好是相同的 2.在本地eclipse下运行项目即可发布(注意(1)数据库连接的是服务器数据库还是本地数据库(2)运行项目前先cl ...

  10. php,单引号与双引号的区别

    代码示例 <?php $s='666'; $s2="999"; $test = 'name{$s} - {$s2}'; $test2 = "name{$s} - { ...