2023年5月16日
原文链接:http://peter.eisentraut.org/blog/2023/05/16/overview-of-icu-collation-settings
作者:Peter Eisentraut(彼得·艾森特劳特)
在 PostgreSQL 中,ICU 的使用正在变得越来越有影响力。ICU 提供的一个好处是它为 collation 提供了许多自定义选项。尽管在PostgreSQL 文档中给出了一些例子,但我一直觉得很难从其中获得完整且易于获取的信息。
所以为了完成这篇文章,我深入研究并查找了所有的 collation 设置,并试图为每一种提供示例。
建议阅读我之前的一篇关于collation 如何工作的文章。但即使你没有阅读过,这也作为对各种应用场景的可行性概述也应该是很有趣的。
作为起点使用的标准文档是《Unicode 技术标准 #35:Unicode 区域数据标记语言 – 第 5 部分:Collation》。该文档(或同系列文档)的主要目的是规定指定区域数据的方法。作为其中的一部分,它还规定了各种设置和其他自定义 collation 的方式。
另一个可以研究的地方是 CLDR
的源代码,这是 ICU 实际使用的数据:https://github.com/unicode-org/cldr/blob/main/common/bcp47/collation.xml
需要注意的是,虽然 Unicode Collation Algorithm(UCA)和其他 Unicode 标准,以及 ICU 和 CLDR 在某种程度上是紧密相关的,但还是存在一些小的记录差异,例如某些设置的默认值有所不同。在实践中,你可以忽略 UCA 的默认值,只关注 ICU 的默认值,但在研究各种文档时,认识到这一点是有价值的。
有两组 collation 自定义设置,我将把这篇文章分为两篇,以保持其易管理性:
und-u-ka-shifted
(见下文)。und-u-co-emoji
和 de-u-co-phonebk
。在这篇文章中,我将更详细地研究参数化设置。我会把其他设置留给[未来的一篇文章](#ICU 排序设置概述,第二部分)。
你也可以在这里遵循参数设置的更技术性的规范:https://www.unicode.org/reports/tr35/tr35-collation.html#Collation_Settings.对于每个设置,我也将引用 CLDR XML 文件中的定义,以便于参考。
关于指定区域(locale)名称的说明:这本身可能是一个专题,但只是为了帮助理解以下内容:在 ICU 中有两种指定区域名称的方式:流行的 BCP 47 语法(如 en-u-kn-true
)和旧的特定于 ICU 的如 en@colNumeric=yes
。在这篇文章中,我将全程使用 BCP 47 语法,但我将使用更老、更冗长的键值作为章节标题,以便导航。
BCP 47 的规范是 RFC 5646。你在下面的例子中看到的 -u-
是注册给 Unicode 的扩展标识符。该扩展的参考是 RFC 6067,它指向 https://cldr.unicode.org/index/bcp47-extension,然后指向上述 XML 文件以获取特定于 collation 的信息。(这些规范涵盖了除 collation 之外的其他区域问题,如日历和数字格式化,但与本文无关。)
我所有的例子都基于 und
,它代表“未确定”,并尽可能使用由 UCA 定义与语言无关的默认顺序。
在我们开始之前的最后一点说明:所有示例都是使用 PostgreSQL 15 和 ICU 72 的。
<key name="ka" description="Collation parameter key for alternate handling" alias="colAlternate">
<type name="noignore" description="Variable collation elements are not reset to ignorable" alias="non-ignorable"/>
<type name="shifted" description="Variable collation elements are reset to zero at levels one through three"/>
key>
这个设置决定了如何处理诸如空白
和标点
这样的可变排序元素。我在之前的一篇文章中详细讨论过这个问题。
以下是一个简单的例子:
create collation a (provider = icu, locale = 'und-u-ka-noignore'); -- default
create collation b (provider = icu, locale = 'und-u-ka-shifted');
select * from (values ('death'), ('deluge'), ('de luge')) _(x)
order by x collate a;
x
---------
de luge
death
deluge
select * from (values ('death'), ('deluge'), ('de luge')) _(x)
order by x collate b;
x
---------
death
de luge
deluge
请注意,ICU 的默认值是“noignore”,而 UCA 的默认值是“shifted”。
有关更多详细信息,请参见前面的文章。
<key name="kb" description="Collation parameter key for backward collation weight" alias="colBackwards">
<type name="true" description="The second level to be backwards" alias="yes"/>
<type name="false" description="No backwards (the second level to be forwards)" alias="no"/>
key>
这个设置导致次要权重(即重音符号)被反向处理,意味着它们从字符串的末尾开始比较。这个设置曾经在法语中使用,但现在已经过时。
示例:
create collation a (provider = icu, locale = 'und-u-kb-false'); -- normal
create collation b (provider = icu, locale = 'und-u-kb-true'); -- backwards
select * from (values ('cote'), ('coté'), ('côte'), ('côté')) _(x)
order by x collate a;
x
------
cote
coté
côte
côté
select * from (values ('cote'), ('coté'), ('côte'), ('côté')) _(x)
order by x collate b;
x
------
cote
côte
coté
côté
正如你在这里所看到的,法语世界在这个问题上似乎存在分歧:
select * from (values ('cote'), ('coté'), ('côte'), ('côté')) _(x)
order by x collate "fr-FR-x-icu";
x
------
cote
coté
côte
côté
select * from (values ('cote'), ('coté'), ('côte'), ('côté')) _(x)
order by x collate "fr-CA-x-icu";
x
------
cote
côte
coté
côté
再次强调,实际上这种做法正在逐渐淘汰。但它是一个有趣的奇特现象。
<key name="kc" description="Collation parameter key for case level" alias="colCaseLevel">
<type name="true" description="The case level is inserted in front of tertiary" alias="yes"/>
<type name="false" description="No special case level handling" alias="no"/>
key>
该设置允许创建忽略重音符号但不忽略大小写的排序规则(collations)。通过 colStrength(ks)设置,可以忽略末尾的权重,例如三级及其后面的权重。但是,如果要忽略重音符号但不忽略大小写,您希望忽略次级权重但保留三级权重。这就是该设置的作用。
示例:
create collation a (provider = icu,
locale = 'und-u-ks-identic', deterministic = false);
create collation b (provider = icu,
locale = 'und-u-kc-true-ks-level1', deterministic = false);
select distinct x collate a
from (values ('foo'), ('Foo'), ('bar'), ('bär')) _(x);
x
-----
foo
bar
bär
Foo
select distinct x collate b
from (values ('foo'), ('Foo'), ('bar'), ('bär')) _(x);
x
-----
Foo
foo
bar
以上可能是这种情况最常见和明显的用例。
更完整和复杂的解释是,该设置在第二级和第三级之间创建了一个额外的权重(相应地称为“2.5”级),用于编码大小写的差异。即使在权重超过主要权重的情况下被忽略,这个虚拟级别仍然保留,就像上面的例子一样。
到目前为止,我一直说第三级编码了大小写,但这并不是它的全部作用。它还编码了字符的其他次要变体。请考虑以下情况:
0061 ; [.2075.0020.0002] # LATIN SMALL LETTER A
24D0 ; [.2075.0020.0006] # CIRCLED LATIN SMALL LETTER A
00E4 ; [.2075.0020.0002][.0000.002B.0002] # LATIN SMALL LETTER A WITH DIAERESIS
0041 ; [.2075.0020.0008] # LATIN CAPITAL LETTER A
24B6 ; [.2075.0020.000C] # CIRCLED LATIN CAPITAL LETTER A
2090 ; [.2075.0020.0015] # LATIN SUBSCRIPT SMALL LETTER A
如果我们打开 colCaseLevel 设置,将插入一个虚拟的“2.5”权重,看起来像这样:
0061 ; [.2075.0020.0001.0002] # LATIN SMALL LETTER A
24D0 ; [.2075.0020.0001.0006] # CIRCLED LATIN SMALL LETTER A
00E4 ; [.2075.0020.0001.0002][.0000.002B.0001.0002] # LATIN SMALL LETTER A WITH DIAERESIS
0041 ; [.2075.0020.0003.0008] # LATIN CAPITAL LETTER A
24B6 ; [.2075.0020.0003.000C] # CIRCLED LATIN CAPITAL LETTER A
2090 ; [.2075.0020.0001.0015] # LATIN SUBSCRIPT SMALL LETTER A
现在我们可以创建一个保留“2.5”权重但忽略原始三级权重及其后面权重的排序规则(collation)。该排序规则保留了大小写的区分,但忽略了其他三级的变体:
create collation c (provider = icu,
locale = 'und-u-kc-true-ks-level2', deterministic = false);
select distinct x collate a
from (values (u&'\0061'),
(u&'\24D0'),
(u&'\00E4'),
(u&'\0041'),
(u&'\24B6'),
(u&'\2090')) _(x);
x
---
ₐ
a
Ⓐ
ⓐ
A
ä
select distinct x collate b
from (values (u&'\0061'),
(u&'\24D0'),
(u&'\00E4'),
(u&'\0041'),
(u&'\24B6'),
(u&'\2090')) _(x);
x
---
a
A
select distinct x collate c
from (values (u&'\0061'),
(u&'\24D0'),
(u&'\00E4'),
(u&'\0041'),
(u&'\24B6'),
(u&'\2090')) _(x);
x
---
ä
a
A
详请参照 https://www.unicode.org/reports/tr35/tr35-collation.html#Case_Parameters。
<key name="kf" description="Collation parameter key for ordering by case" alias="colCaseFirst">
<type name="upper" description="Upper case to be sorted before lower case"/>
<type name="lower" description="Lower case to be sorted before upper case"/>
<type name="false" description="No special case ordering" alias="no"/>
key>
该设置允许您选择是先排序小写字母还是大写字母。默认情况下,小写字母先排序。
示例:
create collation a (provider = icu, locale = 'und-u-kf-lower');
create collation b (provider = icu, locale = 'und-u-kf-upper');
select * from (values ('foo'), ('Foo'), ('bar'), ('Bar')) _(x)
order by x collate "und-x-icu";
x
-----
bar
Bar
foo
Foo
select * from (values ('foo'), ('Foo'), ('bar'), ('Bar')) _(x)
order by x collate a;
x
-----
bar
Bar
foo
Foo
select * from (values ('foo'), ('Foo'), ('bar'), ('Bar')) _(x)
order by x collate b;
x
-----
Bar
bar
Foo
foo
该设置可与 colCaseLevel (kc
) 设置一起使用,也可单独使用。如果 colCaseLevel 为 true,则虚拟的“2.5”权重会被修改以改变大小写的排序顺序。例如,继续之前的例子,对于区域设置 und-u-kc-true-kf-upper
,调整后的虚拟权重将如下所示:
0061 ; [.2075.0020.0003.0002] # LATIN SMALL LETTER A
24D0 ; [.2075.0020.0003.0006] # CIRCLED LATIN SMALL LETTER A
00E4 ; [.2075.0020.0003.0002][.0000.002B.0003.0002] # LATIN SMALL LETTER A WITH DIAERESIS
0041 ; [.2075.0020.0001.0008] # LATIN CAPITAL LETTER A
24B6 ; [.2075.0020.0001.000C] # CIRCLED LATIN CAPITAL LETTER A
2090 ; [.2075.0020.0003.0015] # LATIN SUBSCRIPT SMALL LETTER A
如果 colCaseLevel 为 false,则会修改三级权重。例如,对于区域设置 und-u-kf-upper
(-kc-false
),虚拟权重如下所示:
0061 ; [.2075.0020.30002] # LATIN SMALL LETTER A
24D0 ; [.2075.0020.30006] # CIRCLED LATIN SMALL LETTER A
00E4 ; [.2075.0020.30002][.0000.002B.30002] # LATIN SMALL LETTER A WITH DIAERESIS
0041 ; [.2075.0020.10008] # LATIN CAPITAL LETTER A
24B6 ; [.2075.0020.1000C] # CIRCLED LATIN CAPITAL LETTER A
2090 ; [.2075.0020.30015] # LATIN SUBSCRIPT SMALL LETTER A
但是这最终提供了相同的顺序,除非其他附加选项进一步修改或忽略特定的权重级别。
<key name="kh" deprecated="true" description="Collation parameter key for special Hiragana handling" alias="colHiraganaQuaternary">
<type name="true" description="Hiragana to be sorted before all non-variable on quaternary level" alias="yes"/>
<type name="false" description="No special handling for Hiragana" alias="no"/>
key>
Hiragana 是日本书写系统的一部分。由于我对此不太熟悉,所以我不会在这里提供任何示例。UTS #35 也指出:“已弃用:请改用具有四元关系的规则。”
<key name="kk" description="Collation parameter key for normalization" alias="colNormalization">
<type name="true" description="Convert text into Normalization Form D before calculating collation weights" alias="yes"/>
<type name="false" description="Skip normalization" alias="no"/>
key>
通常,在进行排序之前,字符串会被标准化为 NFD 形式。但是,通过该设置,可以禁用此标准化过程。这可能会提高性能,但在某些情况下可能会产生不正确的行为(或至少是不同的行为)。
默认的排序规则权重设置使得在大多数情况下标准化并不重要。例如,考虑“LATIN SMALL LETTER A WITH DIAERESIS”的组合形式和分解形式:
00E4 ; [.2075.0020.0002][.0000.002B.0002] # LATIN SMALL LETTER A WITH DIAERESIS
0061 ; [.2075.0020.0002] # LATIN SMALL LETTER A
0308 ; [.0000.002B.0002] # COMBINING DIAERESIS
无论字符串是否进行标准化,它们的排序规则权重都将完全相同。
在一些情况下,标准化对排序规则确实有影响。可以在此处详细了解这些情况:https://www.unicode.org/reports/tr35/tr35-collation.html#Normalization_Setting
<key name="kn" description="Collation parameter key for numeric handling" alias="colNumeric">
<type name="true" description="A sequence of decimal digits is sorted at primary level with its numeric value" alias="yes"/>
<type name="false" description="No special handling for numeric ordering" alias="no"/>
key>
该设置会导致数字序列按其数值进行排序(数字排序,也称为自然排序)。
示例:
create collation a (provider = icu, locale = 'und-u-kn-false'); -- standard
create collation b (provider = icu, locale = 'und-u-kn-true'); -- numeric
select * from (values ('A-123'), ('A-12n'), ('A-21'), ('B-100')) _(x)
order by x collate a;
x
-------
A-123
A-12n
A-21
B-100
select * from (values ('A-123'), ('A-12n'), ('A-21'), ('B-100')) _(x)
order by x collate b;
x
-------
A-12n
A-21
A-123
B-100
这在实际中显然非常有用,可以对类似产品或发票编号等几乎是数字的内容进行排序。
它也适用于非 ASCII 数字:
select * from (values ('१२३'), ('२१')) _(x) order by x collate a;
x
-----
१२३
२१
select * from (values ('१२३'), ('२१')) _(x) order by x collate b;
x
-----
२१
१२३
<key name="kr" description="Collation reorder codes" valueType="multiple" alias="colReorder" since="21">
<type name="space" description="Whitespace reordering code, see LDML Part 5: Collation" since="21"/>
<type name="punct" description="Punctuation reordering code, see LDML Part 5: Collation" since="21"/>
<type name="symbol" description="Symbol reordering code (other than currency), see LDML Part 5: Collation" since="21"/>
<type name="currency" description="Currency reordering code, see LDML Part 5: Collation" since="21"/>
<type name="digit" description="Digit (number) reordering code, see LDML Part 5: Collation" since="21"/>
<type name="REORDER_CODE" description="Other collation reorder code — for script, see LDML Part 5: Collation" since="21"/>
key>
该设置允许相对于其他字符块重新排序整个字符块。例如,默认情况下,数字在拉丁字母之前排序,拉丁字母在西里尔字母之前排序。必须选择一种默认顺序,这就是最终的结果。通过该设置,您可以轻松地指定将西里尔字母排在拉丁字母之前,并将数字排在后面,而无需单独重新排序每个字符。在此处存在许多可能的值,包括更多的字母表和其他字符组,如 XML 源中所示。
示例:
create collation a (provider = icu, locale = 'und');
create collation b (provider = icu, locale = 'und-u-kr-cyrl-latn-digit');
select * from (values ('123'), ('România'), ('България'), ('Србија')) _(x)
order by x collate a;
x
----------
123
România
България
Србија
select * from (values ('123'), ('România'), ('България'), ('Србија')) _(x)
order by x collate b;
x
----------
България
Србија
România
123
请注意,特定语言环境可能会覆盖默认的排序顺序。例如,保加利亚语环境将西里尔字母排在首位:
select * from (values ('123'), ('România'), ('България'), ('Србија')) _(x)
order by x collate "bg-x-icu";
x
----------
123
България
Србија
România
请参阅 https://www.unicode.org/reports/tr35/tr35-collation.html#Script_Reordering 获取更多详细信息。
<key name="ks" description="Collation parameter key for collation strength" alias="colStrength">
<type name="level1" description="The primary level" alias="primary"/>
<type name="level2" description="The secondary level" alias="secondary"/>
<type name="level3" description="The tertiary level" alias="tertiary"/>
<type name="level4" description="The quaternary level" alias="quaternary quarternary"/>
<type name="identic" description="The identical level" alias="identical"/>
key>
这指定了要考虑的排序规则权重的级别数量。其他级别将被忽略。这个设置的典型用法是忽略第三级和后续级别来进行不区分大小写的排序(注意,第二级是重音符号,第三级是大小写)。
示例:
create collation a (provider = icu,
locale = 'und-u-ks-identic', deterministic = false);
create collation b (provider = icu,
locale = 'und-u-ks-level2', deterministic = false);
select distinct x collate a
from (values ('foo'), ('Foo'), ('bar'), ('Bar')) _(x);
x
-----
foo
bar
Bar
Foo
select distinct x collate b
from (values ('foo'), ('Foo'), ('bar'), ('Bar')) _(x);
x
-----
foo
bar
您还可以同时忽略大小写和重音符号:
create collation a (provider = icu,
locale = 'und-u-ks-identic', deterministic = false);
create collation b (provider = icu,
locale = 'und-u-ks-level1', deterministic = false);
select distinct x collate a
from (values ('foo'), ('Foo'), ('bar'), ('bär')) _(x);
x
-----
foo
bar
bär
Foo
select distinct x collate b
from (values ('foo'), ('Foo'), ('bar'), ('bär')) _(x);
x
-----
bar
foo
请注意,您不能仅仅使用这个设置来忽略重音符号但不忽略大小写。为了实现这一点,您需要使用 colCaseLevel (kc
) 设置。
<key name="kv" description="Collation parameter key for maxVariable, the last reordering group to be affected by ka-shifted" since="25">
<type name="space" description="Only spaces are affected by ka-shifted" since="25"/>
<type name="punct" description="Spaces and punctuation are affected by ka-shifted (CLDR default)" since="25"/>
<type name="symbol" description="Spaces, punctuation and symbols except for currency symbols are affected by ka-shifted (UCA default)" since="25"/>
<type name="currency" description="Spaces, punctuation and all symbols are affected by ka-shifted" since="25"/>
key>
这个设置指定了哪些字符将成为可变排序元素,并受到 colAlternate (ka
) 设置的影响。在我之前的文章中,我总是谈到“空格和标点符号”,但正如您在这里所看到的,还可以考虑其他字符组。
让我们来看一个例子:
create collation a (provider = icu, locale = 'und-u-ka-shifted');
create collation b (provider = icu, locale = 'und-u-ka-shifted-kv-currency');
select * from (values ('death'), ('deluge'), ('de luge'), ('de$luge')) _(x)
order by x collate a;
x
---------
de$luge
death
de luge
deluge
select * from (values ('death'), ('deluge'), ('de luge'), ('de$luge')) _(x)
order by x collate b;
x
---------
death
de luge
de$luge
deluge
请注意,在第一个示例中,货币符号 $
像一个非可变字符一样排序,出现在字符 a
之前;而在第二个示例中,它受到与空格字符相同的可变处理影响。
默认情况下,在 CLDR(通用语言数据存储库)中,空格和标点符号被视为可变字符,而 UCA(统一排序规则)默认情况下也将符号视为可变字符。
<key name="vt" deprecated="true" description="Collation parameter key for variable top" valueType="multiple" alias="variableTop">
<type name="CODEPOINTS" description="The variable top (one or more Unicode code points: LDML Appendix Q)"/>
key>
这是一个已被弃用的替代方案,与 maxVariable 不同,它不是通过指定字符组名称来指定可变字符,而是通过指定所有具有低于某个特定值(即“top”)的主要权重的字符应该是可变的来选择可变字符。显然,这种方法不如替代方案方便,因此已被弃用。
以下是我们讨论的 collation 设置的总结(已忽略正式废弃的设置):
别名 | 键 | 描述 |
---|---|---|
colAlternate | ka | 交替处理 |
colBackwards | kb | 反向排序权重 |
colCaseLevel | kc | 大小写级别 |
colCaseFirst | kf | 按照大小写排序 |
colNormalization | kk | 标准化 |
colNumeric | kn | 数字处理 |
colReorder | kr | 重新排序码 |
colStrength | ks | 排序强度 |
maxVariable | kv | 最后一个受到 ka-shifted 影响的重新排序组。 |
正如在开始时提到的,我将在将来的另一篇文章中探讨(非参数化)collation
/co
的设置。