4

我正在开发一个需要生成“不区分大小写”的规范化形式的 Unicode 文本的 C 项目。我选择将规范化形式定义为首先转换为规范化形式 NFD,然后应用 Unicode 大小写折叠算法,最后将结果转换为 Unicode 规范化形式 NFC。

我依靠 ICU 的 C API 来实现其 Unicode 表示和实用功能,使用 ICUunorm_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 的规范组合。

所以,两个问题:

  1. 哪个是正确的 NFC 形式?
  2. 如果ICU是正确的,那为什么?
4

1 回答 1

7

ICU是正确的。要了解原因,请查看Unicode 标准第 3 章中定义的规范组合算法:

D117规范组合算法:从编码字符序列(规范分解或兼容性分解)中的第二个字符开始,依次进行到最后一个字符,执行以下步骤:

R1 在编码字符序列中从字符 C 向后(左)查找字符序列中 C 之前的最后一个 Starter L。

R2 如果有这样一个 L,并且 C 没有被 L 阻挡,并且存在一个与序列 <L, C> 规范等价的 Primary Composite P,则将序列中的 L 替换为 P 并从序列中删除 C .

您还必须了解前面的定义,尤其是:

D115阻塞:设 A 和 C 是编码字符序列 <A, ... C> 中的两个字符。当且仅当 ccc(A)=0 并且在编码字符序列中 A 和 C 之间存在某个字符 B,即 <A, ... B, ... C> 和 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 阻挡,但没有等效的主要复合材料,<L, C>所以我们继续下一个字符:

U+0065 U+031C U+0302 U+0303
L             C

现在 C 仍然没有被 L 阻止(这就是你可能误解的),因为ccc(U+031C) = 220 < 230 = ccc(U+0302)存在一个U+00EAU+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 的输出相匹配。

于 2014-06-21T15:51:31.773 回答