字元編碼:Unicode 與 UTF-8 詳解
更新 2026-07-06
你見過:本該讀作 José 的名字變成了 José,或者一個表情符號變成了一堆問號方框。這種亂碼有個名字 —— mojibake(文字化け) —— 而它幾乎總是字元編碼不匹配。本指南講清字元、Unicode 和 UTF-8 究竟是什麼,讓這類 bug 不再神秘。
電腦存的是數字,不是字母
在最底層,電腦只存數字(位元組)。要存文字,我們需要一張約定好的表,把每個字元映射到一個數字,再映射回來。這張表就是 字元編碼。如果一個程式用一張表寫文字,另一個程式用另一張表讀,數字就會被解讀成錯誤的字元 —— 你就得到亂碼。
ASCII:小小的起點
最早廣泛使用的編碼 ASCII 為 128 個字元定義了數字:英文字母、數字、標點和一些控制碼。這對英語夠用,但沒有位置留給 é、ü、ß、你、😀,或世界各語言使用的成千上萬個字元。幾十年裡人人都發明了自己彼此不相容的擴充,這正是文字在系統之間穿行時會壞掉的原因。
Unicode:為每個字元分配一個數字
Unicode 是通用的解決方案:一個單一標準,為每種書寫系統裡的每個字元,外加符號和表情符號,分配一個唯一的數字 —— 稱為 碼點(code point)。碼點寫作 U+ 後跟一個十六進位數:
A → U+0041 é → U+00E9 你 → U+4F60 😀 → U+1F600
關鍵在於,Unicode 定義了 數字,但沒定義如何把它們存成位元組。那是一個單獨的決定 —— 而這正是 UTF-8 登場的地方。
UTF-8:Unicode 如何變成位元組
UTF-8 是把 Unicode 碼點編碼成位元組的一種方式,也是網路壓倒性使用的那種。它精巧的設計在於它是 變長的:
- 128 個 ASCII 字元各佔 1 位元組 —— 所以 UTF-8 與 ASCII 向後相容。
- 大多數帶重音的拉丁字元以及希臘/西里爾字元佔 2 位元組。
- 包括中文、日文和韓文在內的大多數其他文字佔 3 位元組。
- 表情符號和更罕見的字元佔 4 位元組。
這就是為什麼一個「100 字元」的字串並不總是 100 位元組:一個字元可能是好幾個位元組。它也解釋了那個經典的 José bug —— 那是 é 的 UTF-8 位元組,被當成較舊的西方編碼、一位元組一位元組地誤讀了。
字元、碼點和位元組
三個相關卻不同的概念不斷讓人栽跟頭:
- 字元(Character) —— 人所看到的(字母
é)。 - 碼點(Code point) —— 它的 Unicode 數字(
U+00E9)。 - 位元組(Byte) —— 它被儲存的方式(在 UTF-8 中,是兩個位元組
0xC3 0xA9)。
把碼點原樣寫出來看,往往是診斷文字問題最快的辦法 —— 例如,一個原來是不換行空格(U+00A0)的「空格」。我們的 Unicode 轉換器 會顯示任意文字背後的碼點,並在字元與跳脫序列之間轉換。
如何避免亂碼
- 到處都用 UTF-8。 把檔案儲存為 UTF-8 並宣告它 —— 在網頁上用
<meta charset="utf-8">,在 HTTP 中用Content-Type的 charset,在資料庫中用 UTF-8 定序。 - 從頭到尾保持一致。 大多數損壞發生在一方假設了不同編碼的邊界處。
- 為正確的上下文跳脫。 要把特殊字元安全地放進 HTML,用 HTML 實體;要放進 URL,用 URL 編碼。
簡短版:Unicode 給每個字元一個數字,UTF-8 把這些數字變成位元組,而如果人人都同意使用 UTF-8,文字就不再壞掉。
更多 資料格式與編碼
查看全部 資料格式與編碼 指南 →本指南提到的工具