字符編碼是程序員繞不開的坑。本文梳理 ANSI、Unicode、GBK、UTF-8、UTF-16 的來龍去脈、存儲方式、適用場景。
| 編碼 | 全稱 | 覆蓋範圍 | 碼元大小 | 特點 |
|---|---|---|---|---|
| ASCII | American Standard Code for Information Interchange | 128 字符(英字母、數字、符號) | 1 字節 | 基石,高位固定 0 |
| ANSI | American National Standards Institute (誤稱) | 本地化代碼頁(如 CP936=GBK, CP1252=Western) | 1-2 字節 | 非標準統稱,Windows 預設代碼頁 |
| GBK | GuoBiao KuoZhan | 中文簡體+繁體、日韓字符 | 1-2 字節 | 國標擴展,雙字節高位≥0x81,兼容 ASCII |
| Unicode | Universal Character Set | 全球所有字符(149,186+ 字符,含 Emoji) | 抽象碼位 | 字符集標準,不指定存儲方式 |
| UTF-8 | Unicode Transformation Format - 8-bit | 全 Unicode | 1-4 字節 | 變長,ASCII 兼容,網絡傳輸/文件存儲首選 |
| UTF-16 | Unicode Transformation Format - 16-bit | 全 Unicode | 2 或 4 字節 | 定長/代理對,Windows/Java/JS 內存內部表示 |
| UTF-32 | Unicode Transformation Format - 32-bit | 全 Unicode | 4 字節 | 定長,隨機訪問快,空間浪費大 |
「Unicode 編碼」是不嚴謬說法,應說「UTF-8 編碼的 Unicode 字符」。
碼位範圍 字節數 位模板
U+0000 ~ U+007F 1 0xxxxxxx (ASCII 兼容)
U+0080 ~ U+07FF 2 110xxxxx 10xxxxxx
U+0800 ~ U+FFFF 3 1110xxxx 10xxxxxx 10xxxxxx (基本多文種平面 BMP)
U+10000 ~ U+10FFFF 4 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx (輔助平面,含 Emoji)
優勢:ASCII 文本無膨脹,中文 3 字節,Emoji 4 字節,自同步(無需從頭掃描即可找字符邊界)。
BMP 字符 (U+0000~U+FFFF) → 直接存 2 字節
輔助平面 (U+10000+) → 代理對:高代理 (D800-DBFF) + 低代理 (DC00-DFFF) = 4 字節
例:😂 U+1F602 → 0xD83D 0xDE02
| 現象 | 原因 | 修復 |
|---|---|---|
| 中文變「??」或方塊 | UTF-8 數據按 GBK 解碼 | 統一指定 UTF-8 |
| 一個漢字變兩個亂碼 | GBK 數據按 UTF-8 解碼 | 統一指定 GBK 或轉碼 |
| Emoji 顯示為兩個 | UTF-16 代理對被當作兩字符 | 使用支持代理對的字體/庫 |
// Java
String utf8 = new String(gbkBytes, "GBK"); // 解碼
byte[] gbk = utf8Str.getBytes("GBK"); // 編碼
// Python 3
text = gbk_bytes.decode('gbk')
utf8_bytes = text.encode('utf-8')
// C++ (Windows API)
MultiByteToWideChar(CP_UTF8, 0, utf8, -1, wstr, len);
WideCharToMultiByte(CP_ACP, 0, wstr, -1, gbk, len, NULL, NULL);
徹底理解編碼本質,配合「統一內部用 Unicode(UTF-16/32),外部交互用 UTF-8」原則,可消滅 99% 亂碼問題。