排序

将字符串按特定顺序 (字典序、Unicode 码位序、区域设置依赖序等) 重新排列的处理。由于不同语言和文化对"正确顺序"的定义各异,国际化场景中排序规则的配置至关重要。

排序 (sort) 是将字符串列表按特定顺序排列的处理。文件名列表、通讯录姓名、词典词条、搜索结果排名等,凡是涉及文本数据的场景都离不开排序。然而,"正确的顺序"因语言和文化而异,国际化环境下的排序远比表面看起来复杂。

最简单的排序方式是按字符编码的排列顺序。JavaScript 的 Array.sort() 在不传入比较函数时,会先把元素转换成字符串,再按 UTF-16 代码单元序列进行比较。对基本多文种平面 (BMP) 内的字符而言,这与码位顺序一致,但一旦混入 BMP 之外的字符,顺序就会错位。例如把 U+E000 和 U+10000 放在一起排序,按码位顺序应是 U+E000 在前,而默认排序下 U+10000 的代理对首个代码单元是 U+D800,比 U+E000 小,于是 U+10000 反而排在前面。更贴近日常的例子是"Z"(U+005A) 会排在"a"(U+0061) 之前,大小写混合的列表会产生违反直觉的结果。数字字符串也会按字符编码顺序排列为"1, 10, 2, 20, 3",而不是数值顺序。

中文排序有其独特的复杂性。汉字的排列顺序有"拼音序"、"笔画序"、"部首序"等多种标准,具体使用哪种取决于应用场景。通讯录和词典通常按拼音排序,公文中的人名列表常按笔画数排序,传统字典则按部首和笔画数检索。同一个汉字可能有多个读音 (如"行"读 xíng 或 háng),因此基于拼音的排序需要额外的读音数据支持,仅看字符本身无法判断该取哪个音。

ICU (International Components for Unicode) 库是实现区域设置依赖排序的行业标准。JavaScript 的 Intl.Collator 基于 ICU 实现,new Intl.Collator('zh-Hans').compare(a, b) 可以按简体中文的自然顺序排序。德语中带变音符的 ä 是紧排在 a 之后,还是当作 ae 来处理,取决于具体的区域设置,Intl.Collator 能正确处理这类语言特有的规则。

自然排序 (natural sort) 是将字符串中的数字部分按数值解释的排序方式。"file1, file2, file10"按字典序排列为"file1, file10, file2",而自然排序则得到"file1, file2, file10"的数值顺序。这种方式在文件名和版本号排序中能获得更符合直觉的结果。

排序与字符计数的关联在于,生成排序键时往往需要对字符串进行规范化处理。全角与半角的统一、声调符号的处理、大小写的统一等,排序前的预处理可能改变字符串的字符数。在显示排序结果时,通常使用原始字符串 (预处理前),排序键 (预处理后) 仅用于内部比较。

分享这篇文章