{"slug":"unicode-cjk-lap-trinh-xu-ly-103000-ky-tu-han-nom","title":"Unicode CJK Cho Lập Trình Viên: Xử Lý 103,000+ Ký Tự Hán-Nôm","summary":"Hướng dẫn xử lý Unicode CJK cho lập trình viên: các block ký tự, surrogate pairs trong JavaScript, nhận dạng CJK, database collation, và chuyển đổi giản/phồn thể.","excerpt":"Hướng dẫn xử lý Unicode CJK cho lập trình viên: các block ký tự, surrogate pairs trong JavaScript, nhận dạng CJK, database collation, và chuyển đổi giản/phồn thể.","markdown":"# Unicode CJK Cho Lập Trình Viên\nChuyên mục: Lập trình × Ngôn ngữ\n\nXử 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.\n\n## 1. CJK Trong Unicode: Các Block Chính\nUnicode phân bổ ký tự CJK vào nhiều block:\n\n// Block chính\nU+4E00–U+9FFF    CJK Unified      20,992 chars\nU+3400–U+4DBF    Extension A        6,592 chars\nU+20000–U+2A6DF  Extension B       42,720 chars (surrogate pairs!)\nU+2A700–U+2B73F  Extension C        4,149 chars\nU+2B740–U+2B81F  Extension D          222 chars\nU+2B820–U+2CEAF  Extension E        5,762 chars\nU+2CEB0–U+2EBEF  Extension F        7,473 chars\nU+30000–U+3134F  Extension G        4,939 chars\nU+31350–U+323AF  Extension H        4,192 chars\n\n## 2. Surrogate Pairs — Bẫy Cho JavaScript/Java\nKý tự Extension B trở đi (U+20000+) nằm ngoài BMP (Basic Multilingual Plane), cần 2 UTF-16 code units:\n\nconst char = '𠀀'; // U+20000 Extension B\nconsole.log(char.length);        // 2 (KHÔNG PHẢI 1!)\nconsole.log([...char].length);   // 1 (spread operator xử lý đúng)\nconsole.log(char.codePointAt(0)); // 131072 = 0x20000\n\nLuôn dùng codePointAt() thay vì charCodeAt(), và [...str] thay vì str.split('') khi xử lý CJK.\n\n## 3. Nhận Dạng Ký Tự CJK\nfunction isCJK(codePoint) {\nreturn (\n(codePoint >= 0x4E00 && codePoint <= 0x9FFF) ||   // CJK Unified\n(codePoint >= 0x3400 && codePoint <= 0x4DBF) ||   // Ext A\n(codePoint >= 0x20000 && codePoint <= 0x2A6DF) || // Ext B\n(codePoint >= 0x2A700 && codePoint <= 0x2B73F) || // Ext C\n(codePoint >= 0x2B740 && codePoint <= 0x2B81F) || // Ext D\n(codePoint >= 0x2B820 && codePoint <= 0x2CEAF) || // Ext E\n(codePoint >= 0x2CEB0 && codePoint <= 0x2EBEF) || // Ext F\n(codePoint >= 0x30000 && codePoint <= 0x3134F) || // Ext G\n(codePoint >= 0x31350 && codePoint <= 0x323AF) || // Ext H\n(codePoint >= 0xF900 && codePoint <= 0xFAFF)      // Compatibility\n);\n}\n\n## 4. CJK Trong Database: Collation & Indexing\nVấn đề thường gặp khi lưu CJK trong PostgreSQL/MySQL:\n\nCollation: Dùng 'und-x-icu' hoặc 'zh-x-icu' thay vì 'C' để sắp xếp đúng thứ tự Hán tự\nIndexing: pg_trgm hoạt động tốt cho CJK search trong PostgreSQL\nEncoding: Luôn dùng UTF-8, kiểm tra connection encoding\nFull-text search: CJK cần tokenizer đặc biệt (jieba, mecab, ICU)\n## 5. Giản Thể ↔ Phồn Thể Conversion\nChuyể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:\n\n// 发 (giản) có thể là:\n// 發 (phát - phát triển) hoặc 髮 (phát - tóc)\n// Cần context để phân biệt!\n// OpenCC library xử lý tốt vấn đề này\n","html":"<h1>Unicode CJK Cho Lập Trình Viên</h1><p>Chuyên mục: Lập trình × Ngôn ngữ</p><p>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.</p><h2>1. CJK Trong Unicode: Các Block Chính</h2><pre><code>Unicode phân bổ ký tự CJK vào nhiều block:</code></pre><pre><code>// Block chính</code></pre><pre><code>U+4E00–U+9FFF    CJK Unified      20,992 chars</code></pre><pre><code>U+3400–U+4DBF    Extension A        6,592 chars</code></pre><pre><code>U+20000–U+2A6DF  Extension B       42,720 chars (surrogate pairs!)</code></pre><pre><code>U+2A700–U+2B73F  Extension C        4,149 chars</code></pre><pre><code>U+2B740–U+2B81F  Extension D          222 chars</code></pre><pre><code>U+2B820–U+2CEAF  Extension E        5,762 chars</code></pre><pre><code>U+2CEB0–U+2EBEF  Extension F        7,473 chars</code></pre><pre><code>U+30000–U+3134F  Extension G        4,939 chars</code></pre><p>U+31350–U+323AF  Extension H        4,192 chars</p><h2>2. Surrogate Pairs — Bẫy Cho JavaScript/Java</h2><pre><code>Ký tự Extension B trở đi (U+20000+) nằm ngoài BMP (Basic Multilingual Plane), cần 2 UTF-16 code units:</code></pre><pre><code>const char = '𠀀'; // U+20000 Extension B</code></pre><pre><code>console.log(char.length);        // 2 (KHÔNG PHẢI 1!)</code></pre><pre><code>console.log([...char].length);   // 1 (spread operator xử lý đúng)</code></pre><p>console.log(char.codePointAt(0)); // 131072 = 0x20000</p><p>Luôn dùng codePointAt() thay vì charCodeAt(), và [...str] thay vì str.split('') khi xử lý CJK.</p><pre><code>3. Nhận Dạng Ký Tự CJK</code></pre><pre><code>function isCJKcodePoint {</code></pre><pre><code>  return (</code></pre><pre><code>    codePoint &amp;gt;= 0x4E00 &amp;amp;&amp;amp; codePoint &amp;lt;= 0x9FFF ||   // CJK Unified</code></pre><pre><code>    codePoint &amp;gt;= 0x3400 &amp;amp;&amp;amp; codePoint &amp;lt;= 0x4DBF ||   // Ext A</code></pre><pre><code>    codePoint &amp;gt;= 0x20000 &amp;amp;&amp;amp; codePoint &amp;lt;= 0x2A6DF || // Ext B</code></pre><pre><code>    codePoint &amp;gt;= 0x2A700 &amp;amp;&amp;amp; codePoint &amp;lt;= 0x2B73F || // Ext C</code></pre><pre><code>    codePoint &amp;gt;= 0x2B740 &amp;amp;&amp;amp; codePoint &amp;lt;= 0x2B81F || // Ext D</code></pre><pre><code>    codePoint &amp;gt;= 0x2B820 &amp;amp;&amp;amp; codePoint &amp;lt;= 0x2CEAF || // Ext E</code></pre><pre><code>    codePoint &amp;gt;= 0x2CEB0 &amp;amp;&amp;amp; codePoint &amp;lt;= 0x2EBEF || // Ext F</code></pre><pre><code>    codePoint &amp;gt;= 0x30000 &amp;amp;&amp;amp; codePoint &amp;lt;= 0x3134F || // Ext G</code></pre><pre><code>    codePoint &amp;gt;= 0x31350 &amp;amp;&amp;amp; codePoint &amp;lt;= 0x323AF || // Ext H</code></pre><pre><code>    codePoint &amp;gt;= 0xF900 &amp;amp;&amp;amp; codePoint &amp;lt;= 0xFAFF      // Compatibility</code></pre><pre><code>  );</code></pre><p>}</p><h2>4. CJK Trong Database: Collation &amp; Indexing</h2><p>Vấn đề thường gặp khi lưu CJK trong PostgreSQL/MySQL:</p><ul><li>Collation: Dùng 'und-x-icu' hoặc 'zh-x-icu' thay vì 'C' để sắp xếp đúng thứ tự Hán tự</li><li>Indexing: pg_trgm hoạt động tốt cho CJK search trong PostgreSQL</li><li>Encoding: Luôn dùng UTF-8, kiểm tra connection encoding</li><li>Full-text search: CJK cần tokenizer đặc biệt (jieba, mecab, ICU)</li><h2>5. Giản Thể ↔ Phồn Thể Conversion</h2></ul><pre><code>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:</code></pre><pre><code>// 发 (giản) có thể là:</code></pre><pre><code>// 發 (phát - phát triển) hoặc 髮 (phát - tóc)</code></pre><pre><code>// Cần context để phân biệt!</code></pre><p>// OpenCC library xử lý tốt vấn đề này</p>","tags":["JCK Font","Lập trình","Ngôn ngữ"],"author":"Quản trị viên","publishedAt":"2026-04-04T07:37:28.917Z","updatedAt":"2026-10-14T11:49:05.709Z","published_at":"2026-04-04T07:37:28.917Z","updated_at":"2026-10-14T11:49:05.709Z","view_count":407,"canonical":"https://wiki.quizzman.com/wiki/unicode-cjk-lap-trinh-xu-ly-103000-ky-tu-han-nom","url":"https://wiki.quizzman.com/wiki/unicode-cjk-lap-trinh-xu-ly-103000-ky-tu-han-nom","markdownUrl":"https://wiki.quizzman.com/api/articles/unicode-cjk-lap-trinh-xu-ly-103000-ky-tu-han-nom.md","apiUrl":"https://wiki.quizzman.com/api/articles/unicode-cjk-lap-trinh-xu-ly-103000-ky-tu-han-nom"}