Unicode CJK Cho Lập Trình Viên: Xử Lý 103,000+ Ký Tự Hán-Nôm

Unicode CJK Cho Lập Trình Viên

Chuyên mục: Lập trình × Ngôn ngữ

Xử lý ký tự CJK (Chinese-Japanese-Korean) trong lập trình đặt ra nhiều thách thức: từ encoding, surrogate pairs, đến normalization. Bài viết tổng hợp kiến thức cần thiết cho developer.

1. CJK Trong Unicode: Các Block Chính

Unicode phân bổ ký tự CJK vào nhiều block:

// Block chính
U+4E00–U+9FFF    CJK Unified      20,992 chars
U+3400–U+4DBF    Extension A        6,592 chars
U+20000–U+2A6DF  Extension B       42,720 chars (surrogate pairs!)
U+2A700–U+2B73F  Extension C        4,149 chars
U+2B740–U+2B81F  Extension D          222 chars
U+2B820–U+2CEAF  Extension E        5,762 chars
U+2CEB0–U+2EBEF  Extension F        7,473 chars
U+30000–U+3134F  Extension G        4,939 chars

U+31350–U+323AF Extension H 4,192 chars

2. Surrogate Pairs — Bẫy Cho JavaScript/Java

Ký tự Extension B trở đi (U+20000+) nằm ngoài BMP (Basic Multilingual Plane), cần 2 UTF-16 code units:

const char = '𠀀'; // U+20000 Extension B
console.log(char.length);        // 2 (KHÔNG PHẢI 1!)
console.log([...char].length);   // 1 (spread operator xử lý đúng)

console.log(char.codePointAt(0)); // 131072 = 0x20000

Luôn dùng codePointAt() thay vì charCodeAt(), và [...str] thay vì str.split('') khi xử lý CJK.

3. Nhận Dạng Ký Tự CJK

function isCJK(codePoint) {
  return (
    (codePoint >= 0x4E00 && codePoint <= 0x9FFF) ||   // CJK Unified
    (codePoint >= 0x3400 && codePoint <= 0x4DBF) ||   // Ext A
    (codePoint >= 0x20000 && codePoint <= 0x2A6DF) || // Ext B
    (codePoint >= 0x2A700 && codePoint <= 0x2B73F) || // Ext C
    (codePoint >= 0x2B740 && codePoint <= 0x2B81F) || // Ext D
    (codePoint >= 0x2B820 && codePoint <= 0x2CEAF) || // Ext E
    (codePoint >= 0x2CEB0 && codePoint <= 0x2EBEF) || // Ext F
    (codePoint >= 0x30000 && codePoint <= 0x3134F) || // Ext G
    (codePoint >= 0x31350 && codePoint <= 0x323AF) || // Ext H
    (codePoint >= 0xF900 && codePoint <= 0xFAFF)      // Compatibility
  );

}

4. CJK Trong Database: Collation & Indexing

Vấn đề thường gặp khi lưu CJK trong PostgreSQL/MySQL:

  • Collation: Dùng 'und-x-icu' hoặc 'zh-x-icu' thay vì 'C' để sắp xếp đúng thứ tự Hán tự
  • Indexing: pg_trgm hoạt động tốt cho CJK search trong PostgreSQL
  • Encoding: Luôn dùng UTF-8, kiểm tra connection encoding
  • Full-text search: CJK cần tokenizer đặc biệt (jieba, mecab, ICU)

5. Giản Thể ↔ Phồn Thể Conversion

Chuyển đổi giản thể/phồn thể KHÔNG phải 1-1 mapping. Nhiều ký tự giản thể map đến nhiều ký tự phồn thể tùy context:

// 发 (giản) có thể là:
// 發 (phát - phát triển) hoặc 髮 (phát - tóc)
// Cần context để phân biệt!

// OpenCC library xử lý tốt vấn đề này