UTF-8 如何運作?ASCII 兼容、位元組與 UTF-16 的分別
DDEVELOPER
開發發布: 9 分鐘閱讀

UTF-8 如何運作?ASCII 兼容、位元組與 UTF-16 的分別

UTF-8 把 Unicode 字元編成位元組序列,既保留 ASCII 的原有數值,也能用可變長度表示其他字元。不過,檔案的位元組數、碼位數及屏幕上看到的字元數並不相同。

RFC 3629 記載,Ken Thompson 在 1992 年 9 月按 Rob Pike 提出的設計要求構思這種編碼。理解它的位元結構,便能看出為何 Plan 9 可以在保留不少既有處理的情況下採用 Unicode。

日文原文發布: 2026-05-02

ASCII 的 128 個位置不足以容納各種文字

1960 年代制定的 ASCII 使用 7 位元,共有 128 個位置,涵蓋英文字母、數字、基本符號及控制碼。它不能直接表示中文字,連法文的部分重音字母也不在其中。

其後出現 Shift_JIS、EUC-KR、Big5、ISO 8859 系列及 Windows-1252 等編碼。同一組位元組若按錯誤編碼解讀,就可能成為亂碼。Unicode 的構想是為文字分配一致的碼位,而非任由每個系統對同一數值作不同解釋。

1991 年:Unicode 與早期 16 位元表示

Unicode Consortium 於 1991 年 1 月 3 日成立,同年 10 月出版 Unicode 1.0 第一冊。初期設計認為 16 位元的編碼空間已足夠;這與後來可變長度的 UTF-16 要分開理解。

碼位是編號,如何儲存成位元組則是另一層問題。例如 ASCII 的 A 原為 0x41,16 位元表示可能是 0x00 0x41 或 0x41 0x00,需要指定位元組次序。當中的零位元組也會令把 0x00 當作字串結尾的 C 程式提早停止。

只計 ASCII 文字的資料本身,16 位元表示需要的空間是原來的兩倍。Unix 與 Plan 9 的既有檔名、分隔符號及字串處理,因此需要兼容性更高的方案。

1992 年的設計與其後標準化

RFC 3629 將 Thompson 列為設計者,Pike 則提出設計要求。標準化經 X/Open 的國際化小組推進,期間曾用 FSS-UTF、UTF-2 和 UTF-8 等名稱。餐廳軼事的細節不宜與規格記載的歷史混為一談。

核心要求之一,是保留 ASCII 分隔符號及既有位元組處理的用途。這減少了遷移時必須重寫的程式,但不代表原本按位元組計算的程式會自動變成按字元計算。

1 至 4 位元組的結構與限制

如表格未能完整顯示,可左右捲動。

碼位範圍位元組位元模式
U+0000–U+007F10xxxxxxx
U+0080–U+07FF2110xxxxx 10xxxxxx
U+0800–U+FFFF,排除 U+D800–U+DFFF31110xxxx 10xxxxxx 10xxxxxx
U+10000–U+10FFFF411110xxx 10xxxxxx 10xxxxxx 10xxxxxx

x 表示承載碼位數值的位元。ASCII 字元保留原有位元組,而且 ASCII 位元組不會藏在多位元組序列內。延續位元組以 10 開頭;在有效 UTF-8 序列中,最多向前回溯 3 個位元組便可找到起點。這是自同步特性,不是修復損壞資料的保證。

只檢查開頭位元並不足夠。C0 80 是過長編碼,ED A0 80 對應代理碼,F4 90 80 80 超出上限;不完整序列亦屬無效。U+D800–U+DFFF 不是 Unicode 純量值,不能直接編成 UTF-8。

UTF-8 沒有大端序或小端序之分。檔案開頭的 EF BB BF 可作 BOM 編碼標記,卻不是位元組次序指示。除了 U+0000,其他字元的 UTF-8 編碼不會包含 0x00;C 的 strlen() 仍然只計位元組,strcmp() 也不會因此具備語言排序規則。

中文字不一定只佔 3 個位元組

許多常用 CJK 漢字及日文假名的碼位在 UTF-8 中佔 3 個位元組,但補充平面的漢字和部分表情符號需要 4 個。多個碼位組合成的可見字元,更不能套用固定大小。

以日文資料為例,部分能用 Shift_JIS 或 EUC-JP 表示的字元,在這些舊編碼中佔 2 個位元組,在 UTF-8 中則佔 3 個。這不是所有中文字或所有舊編碼的通則。HTML、CSS 和 JavaScript 中亦有大量 ASCII,整體大小及 gzip/Brotli 壓縮後的差距應以實際內容量測。

收發雙方採用 UTF-8 並檢查無效序列,可以減少編碼混淆;若仍以另一種編碼讀取,或把替代字元重新存檔,資料一樣可能損失。這與把 Shift_JIS 的尾隨位元組誤當 ASCII 符號的「5C 問題」是不同情況。

RFC 3629 為何只保留 4 位元組上限?

1998 年 1 月的 RFC 2279 曾容許最長 6 位元組、直至 U+7FFFFFFF 的序列。2003 年 11 月的 RFC 3629 取代該文件,將上限收窄至 4 位元組及 U+10FFFF。

Unicode 與 ISO 10646 將碼位空間限制於 U+10FFFF,與 UTF-16 的代理對範圍一致。對有效 Unicode 純量值序列而言,UTF-8、UTF-16 和 UTF-32 之間可作無損轉換。編碼空間仍有未分配位置,不能把空間大小當作已收錄字元數。

網頁的宣告要與檔案內容一致

HTML 的編碼宣告、HTTP charset 和檔案內的實際位元組必須相符。只把宣告改成 UTF-8 並不會轉換檔案。可用二進位編輯器檢查實際位元組。

網站採用某種編碼的比例與網絡流量佔比是不同指標。選擇編碼時,應先確認兼容性、交換約定及資料完整性,不宜引用測量對象不明的普及率。

UTF-8、UTF-16、UTF-32 如何計算長度?

如表格未能完整顯示,可左右捲動。

編碼每個 Unicode 純量值ASCIIU+3042常見用途
UTF-81–4 位元組1 位元組3 位元組網頁、檔案、API
UTF-161 或 2 個 16 位元碼元2 位元組2 位元組Windows API、Java 及 JavaScript 字串
UTF-324 位元組4 位元組4 位元組按碼位處理的內部表示

以上不計 BOM。Unicode 2.0(1996)引入代理碼與 UTF-16 機制;首次加入補充字元則是 Unicode 3.1(2001)。UTF-16 的代理對由兩個 16 位元碼元組成。

"😀".length === 2 計的是 JavaScript 字串的 UTF-16 碼元;new TextEncoder().encode("😀").length === 4 計的才是 UTF-8 位元組。補充漢字亦可能需要 4 位元組。結合序列包含多個碼位,因此 UTF-32 也不能令每個可見字元的大小固定。

重點整理

  • UTF-8 以 1 至 4 位元組表示 Unicode 純量值,並保持 ASCII 兼容。
  • 1992 年的設計與 2003 年的範圍修訂是不同階段。
  • 代理碼、過長編碼、超出上限及不完整序列都不是有效 UTF-8。
  • 分清位元組、碼元、碼位與屏幕上的字元。
  • 與接收端約定 charset、BOM 及錯誤處理,並核對轉換結果。

參考資料及來源

編輯說明

本文使用 AI 協助編輯,並在發布前由編輯核對。內容仍可能包含事實、理解或時效上的錯誤;作出重要決定前,請查閱所列的一手資料或官方文件。

相關工具

相關文章