我正在开发一个C项目,需要生成“不区分大小写”的规范化形式的Unicode文本。我选择将规范化形式定义为首先转换为NFD规范化,然后应用Unicode案例折叠算法,最后将结果转换为NFC规范化形式。
我依靠ICU的C API来实现其Unicode表示和实用功能,并且使用ICU的unorm_normalize()
和u_strFoldCase()
函数实现我的方案相当简单。但是我的一个测试失败了,我不明白为什么。 ICU似乎产生了与我预期不同的NFC形式。
输入序列由以下BMP代码点组成:
U+0020, U+1EA5, U+0328, U+1EC4, U+031C
通过调试器,我确定ICU和我同意案例折叠后的中间结果:
U+0020 U+0061 U+0328 U+0302 U+0301 U+0065 U+031C U+0302 U+0303
特别注意,早期转换为NFD将U + 031C转换为U + 1EC4分解的中间位置,根据相关字符的相对CCC编号进行适当调整。这是我试图测试的一部分。
现在好的部分:根据ICU,折叠字符序列的NFC标准化是
U+0020 U+0105 U+0302 U+0301 U+1ec5 U+031C
虽然我认为它应该是
U+0020 U+0105 U+0302 U+0301 U+0065 U+031C U+0302 U+0303
因为三个尾随组合字符已经按规范顺序排列,并且没有U + 0065和U + 031C的规范组合。
所以,有两个问题:
答案 0 :(得分:6)
ICU是正确的。要了解原因,请查看chapter 3 of the Unicode Standard中定义的规范组合算法:
D117 规范组合算法:从编码字符序列(规范分解或兼容性分解)中的第二个字符开始,然后按顺序进行到最后一个字符,执行以下步骤:
R1从字符C中回溯(左)编码字符序列,找到字符序列中C之前的最后一个Starter L.
R2如果存在这样的L,并且C没有被L阻挡,并且存在规范等效于序列< L,C>的一次复合P,则在序列中用P替换L并删除C来自序列。
您还必须了解前面的定义,尤其是:
D115 被阻止:令A和C为编码字符序列中的两个字符< A,... C>。当且仅当ccc(A)= 0并且在编码字符序列中A和C之间存在一些字符B,即< A,... B,...... C>时,C被A阻止。 ccc(B)= 0或ccc(B)> = ccc(C)。
现在考虑输入序列的以下子字符串:
U+0065 U+031C U+0302 U+0303
我们从字符U+031C
开始,然后回到U+0065
的最后一个启动器:
U+0065 U+031C U+0302 U+0303
L C
显然C没有被阻止,但是没有与<L, C>
相当的主复合,所以我们继续下一个字符:
U+0065 U+031C U+0302 U+0303
L C
现在C仍然没有被L阻止(这可能是您误解的)因为ccc(U+031C) = 220 < 230 = ccc(U+0302)
并且存在与U+00EA
等效的主要组合U+0065 U+0302
。所以我们替换L并删除C:
U+00EA U+031C U+0303
L C
同样,C不会被L阻止,主要合成U+1EC5
等同于U+00EA U+0303
,因此合成的最终结果是:
U+1EC5 U+031C
这与ICU的输出相匹配。