Unicode CJK 程式设计指南:处理超过 103,000 个汉字与汉喃字

分类:程式设计 × 语言

处理 CJK(Chinese–Japanese–Korean)文字时,开发者会遇到许多不同于一般 ASCII 或拉丁字母的问题,例如字元编码(Encoding)、Surrogate Pair、Unicode Normalization,以及资料库排序(Collation)等。

本文整理程式设计中处理 Unicode CJK 所需的重要观念。


1. Unicode 中的 CJK 区块

Unicode 将 CJK 字元分散于多个不同区块:

U+4E00–U+9FFF      CJK Unified Ideographs          20,992 字
U+3400–U+4DBF      Extension A                      6,592 字
U+20000–U+2A6DF    Extension B                     42,720 字(需 Surrogate Pair)
U+2A700–U+2B73F    Extension C                      4,149 字
U+2B740–U+2B81F    Extension D                        222 字
U+2B820–U+2CEAF    Extension E                      5,762 字
U+2CEB0–U+2EBEF    Extension F                      7,473 字
U+30000–U+3134F    Extension G                      4,939 字
U+31350–U+323AF    Extension H                      4,192 字

目前 Unicode 已收录超过 103,000 个 CJK 汉字


2. Surrogate Pair:JavaScript 与 Java 最常见的陷阱

Extension B(U+20000) 起,所有字元皆位于 BMP(Basic Multilingual Plane) 之外。

UTF-16 必须使用 两个 Code Unit 才能表示一个字元。

const char = '𠀀'; // U+20000

console.log(char.length);          // 2(不是 1)
console.log([...char].length);     // 1
console.log(char.codePointAt(0));  // 131072 = 0x20000

因此:

  • 使用 codePointAt(),不要使用 charCodeAt()
  • 使用 [...str]for...of 遍历字串
  • 避免使用 split('') 处理 CJK 字元

这些都是避免 UTF-16 错误的重要做法。


3. 判断 CJK 字元

可以依照 Unicode Code Point 判断是否属于 CJK:

function isCJK {
  return (
    codePoint >= 0x4E00 && codePoint <= 0x9FFF ||
    codePoint >= 0x3400 && codePoint <= 0x4DBF ||
    codePoint >= 0x20000 && codePoint <= 0x2A6DF ||
    codePoint >= 0x2A700 && codePoint <= 0x2B73F ||
    codePoint >= 0x2B740 && codePoint <= 0x2B81F ||
    codePoint >= 0x2B820 && codePoint <= 0x2CEAF ||
    codePoint >= 0x2CEB0 && codePoint <= 0x2EBEF ||
    codePoint >= 0x30000 && codePoint <= 0x3134F ||
    codePoint >= 0x31350 && codePoint <= 0x323AF ||
    codePoint >= 0xF900 && codePoint <= 0xFAFF
  );
}

其中包含:

  • CJK Unified Ideographs
  • Extension A–H
  • CJK Compatibility Ideographs

4. 资料库中的 CJK:Collation 与索引

在 PostgreSQL 或 MySQL 储存 CJK 时,常见注意事项包括:

  • Collation(排序规则)

建议使用 und-x-icuzh-x-icu,避免使用 C 排序规则,以获得较合理的汉字排序。

  • Indexing(索引)

PostgreSQL 可搭配 pg_trgm 建立 CJK 搜寻索引。

  • Encoding(编码)

建议全程使用 UTF-8,并确认资料库连线的 Encoding 设定一致。

  • Full-text Search(全文搜寻)

CJK 并没有空格分词,因此通常需要使用专门的断词器,例如:

- jieba - MeCab - ICU Tokenizer


5. 简体与繁体转换

简体与繁体之间并不是一对一对应。

同一个简体字可能依语境对应不同繁体字。

例如:

可能转换为:

发(发展、发现)

或:

发(头发)

因此,

简繁转换不能只依靠单纯字典映射,

还需要考虑上下文语意。

目前 OpenCC 是处理简繁转换最成熟且广泛使用的开源工具之一。