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ínhU+4E00–U+9FFF CJK Unified 20,992 charsU+3400–U+4DBF Extension A 6,592 charsU+20000–U+2A6DF Extension B 42,720 chars (surrogate pairs!)U+2A700–U+2B73F Extension C 4,149 charsU+2B740–U+2B81F Extension D 222 charsU+2B820–U+2CEAF Extension E 5,762 charsU+2CEB0–U+2EBEF Extension F 7,473 charsU+30000–U+3134F Extension G 4,939 charsU+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 Bconsole.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ự CJKfunction isCJKcodePoint { 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