UAX#15第9节NFC快速检测示例增补字符相关代码正确性问询
关于UAX#15 NFC快速校验示例代码的疑问解答
首先给出核心结论:你对if (Character.isSupplementaryCodePoint(ch)) ++i;这行逻辑的作用存在误解,UAX#15给出的这段示例代码完全符合Unicode规范,没有任何错误。
这行逻辑的实际作用
你误以为这行是跳过下一个码点的校验,本质是混淆了Java中String的存储单位和Unicode码点的概念:
- Java的
String采用UTF-16编码存储,每个下标i对应的是1个16位的代码单元,不是1个Unicode码点 - 增补码点(≥0x10000)需要占用2个连续的代码单元(高代理项+低代理项)存储
- 调用
codePointAt(i)拿到完整的增补码点后,必须手动把下标i额外加1,跳过该增补码点占用的第二个代码单元,否则下一轮循环会把低代理项当成独立的码点处理,属于完全错误的逻辑
这行是Java语言层面的存储适配代码,和Unicode归一化规则没有任何关系,不存在任何漏检下一个码点的情况:处理完当前增补码点后,下一轮循环的i会自动落到下一个独立码点的起始位置,不管后续码点是BMP还是增补字符,都会正常校验CCC排序规则和NFC_QC属性,你担心的漏检场景根本不会出现。
增补字符校验逻辑的合法性依据
这段逻辑本身和Unicode归一化规则无关,所以不需要依赖你猜测的「CCC>0的增补字符NFC_QC为No」的假设。所有增补码点的校验逻辑和BMP码点完全一致:
- 正常读取完整码点
- 校验和前一个码点的CCC排序是否符合规范
- 校验该码点的NFC_QC属性
完全不会因为是增补字符就跳过任何校验步骤。
移除该行逻辑的后果
如果删掉这行代码,程序一定会出现错误:
- 遇到增补码点时,下一轮循环会读取该码点的低代理项,低代理项本身不是合法的独立Unicode码点
- 对低代理项计算CCC、查询NFC_QC属性得到的都是无意义的错误值,最终快速校验的结果完全不可信,甚至可能触发运行时异常。
以下为原代码片段对照:
public int quickCheck(String source) { short lastCanonicalClass = 0; int result = YES; for (int i = 0; i < source.length(); ++i) { int ch = source.codepointAt(i); if (Character.isSupplementaryCodePoint(ch)) ++i; short canonicalClass = getCanonicalClass(ch); if (lastCanonicalClass > canonicalClass && canonicalClass != 0) { return NO; } int check = isAllowed(ch); if (check == NO) return NO; if (check == MAYBE) result = MAYBE; lastCanonicalClass = canonicalClass; } return result; }
内容的提问来源于stack exchange,提问作者Dave
相关产品推荐
相关产品推荐

