文字編碼混沌時代

你的純文字,他的亂碼

只要 Windows 還在使用 ANSI 編碼的一天,所謂「純文字」文本必然不單純。從最基本的 .txt 文件、網頁瀏覽、到遊戲對話框,凡需要顯示文本的地方,文字編碼(Encoding)就無所不在。這是一種電腦儲存文字的方式:將一個個字元(Character)轉換成數字的過程。

實際上,ANSI 也並非一種編碼方式,只是一套 Windows 自訂規定,以制定各系統語言環境下的預設文字編碼。其中又以 Code Page 來區分,例如繁體中文區的 Big5(cp950)、簡體中文區的 GB2312(cp936)、日文區的 Shift-JIS(cp932),詳見字碼頁識別碼 – Win32 apps1

在介紹編碼方式之前先來解釋一下電腦顯示文字的原理。

將字元編碼成數字

追根究底,所謂字其實是圖。甲骨文、楔形文字、象形文字等文字起源,其實都是圖。所以古人刻石畫骨鑿壁。但電腦只看得懂數字2,所以我們必須為字元配對號碼。這樣電腦就可以通過此號碼來向字型(Font)索引字符(Glyph)來顯示。

但猛得就想要為所有字元編碼顯然不切實際,於是乎各國就有一幫人跳出來提出字元集(Character Set)標準,收集各國會用到的常用字。例如英文圈「ASCII」、繁體中文圈「Big5」、或者日文圈的「JIS X 0208」,以及族繁不及備載。

但憑什麼你就成為標準?於是除了上列字集以外,還有許多變體字集,例如「Extended ASCII」、「倚天外字集」、「JIS X 0213」等。雖然他們包含相同的編碼區域(例如 ASCII 編錄的 128 個字元),但也僅止於此。所以只要不同國家的人檔案傳來傳去就會亂碼3。在文字編碼的戰國時代之下,Unicode 來了,致力於收錄世界上所有文字。

以下簡介一些個人接觸過的字元集及其編碼方式。

常見字元集/文字編碼

需要多少 Bytes(位元組),才能完整收錄字集裡的字元呢?

ASCII

這個問題在美國人眼裡很簡單。以英文文章為例,26 個字母大小寫加上一些符號及換行等控制符,ASCII 字集編錄了 128 個號碼,只使用 1 bytes(256 個空位中只用了一半)。例如 . 編碼為 46、a 編碼為 97。

ASCII 標準包含字元集、編碼。

Big5(大五碼)

來到中文圈,事情就變得複雜許多。《康熙字典》收錄了 47,035 字,教育部公告的《常用國字標準字體表》中也收錄了 4,808 個常用字。1 byte 不夠,那就 2 bytes,理論上最多能收錄 65,536 字。Big5 最終收錄 1 萬多字元,但只要遇到冷僻字、人名常用字(例如:堃、峯)Big5 還是經常缺字。

Big5 標準包含字元集、編碼。延伸閱讀:字嗨 – 關於Big5

Shift-JIS、EUC-JP

使用漢字的日本也選擇使用了 2 bytes 來解決問題。例如 Windows 使用的改版 Shift-JIS,及 Linux 使用的 EUC-JP。Shift-JIS、EUC-JP 做為兩種不同編碼標準,各自收錄了不同字元集(細分為英數、假名、漢字集等標準)。細節可以參考文章とほほの文字コード入門 – とほほのWWW入門

Unicode

Unicode 又稱萬國碼,旨在統一全世界的文字編碼。在此以前,字元被編在哪個號碼——Unicode 中稱為碼位(Code Point),存儲在記憶體上就是同個號碼。但 Unicode 中合法碼位多達 1,114,112,使用等長的 2 bytes 編碼方式(例如 UTF-32)將非常浪費儲存空間,因此也發展出一套可變長度的編碼方式來記錄文字編號(例如 UTF-8),編碼後的一個字元大小為 1 byte 或以上。

Unicode Normalization Forms

Unicode 一個特色,就是支援組合技:可以用多重字符或控制符(2 個以上連續碼位)來變更字的樣式。所以可以輕鬆做出怪力亂神的花樣字(或噴射字), ҉例҉如҉這҉樣҉。҉

但也因此產生新的問題。

帶符號字母的正規化

  • NFC:帶符號字母自己一個碼位。
  • NFD:透過組合技(多個連續碼位)來表示帶符號字母。

正規化不相容會造成搜尋失效、檔案打不開等問題,尤其跨平台傳檔案時特別容易發生(MacOS 用的 UTF-8-Mac 採用 NFD、Windows 則使用 NFC)。若你只會使用中英文大概永遠遇不到這題。之前我遇過一次,朋友用 MacOS 傳給我一個日文名壓縮檔說打不開,但我在 Windows 上可以正常解壓縮⋯⋯,查了超久才發現是檔名編碼的問題。有機會再寫文章說明 QQ

異體字選擇器(Variation Selector)

  • IVS:透過增加控制符碼位來改變字形。
  • IVD:透過字型的選擇功能來改變字形(例如:Adobe-Japan1 字符集)。

這個問題可能設計師會在意,至少我還沒踩過這個坑。

延伸閱讀:

字型與文字編碼

在製作字型時,首先會決定要造多少字,又由於前述的字元集多半為字元標準,並非字形標準——只對字元編碼,而非字形(每個字的具體長相),所以又延伸出一堆字形用的字符集(例如 Adobe-Japan1)⋯⋯4

延伸閱讀:


看對這裡,相信大家已經迷失在各種標準的霾霧之中⋯⋯這還只是冰山一角 🤣(說不定大家都是為了使用 Emoji 才開始轉用 Unicode XD)

幸好 Unicode 出現,使得作業系統如今在顯示文字時,才能統一先轉成 Unicode,再查找字體裡的字符。若缺字則要嘛換字體,要嘛放棄顯示,顯示為豆腐方框(Tofu)。同樣操作也發生在複製貼上,系統會根據目標文檔編碼格式,去轉換成合用的編碼。所以現在你能正確地看到本文,也要歸功於此網頁標記使用 UTF-8,才不用瀏覽器費心去猜測文字編碼(還經常猜錯)。

「書同文」看似理所當然,電腦上的文字顯示卻要等到 1991 年——Unicode 第一版發佈——過後,才開始逐漸統一。每次遇到亂碼,都在見證歷史 😌

進階閱讀

References

Footnotes

  1. 老實說,Windows 中的各 Code Page 有各自對字元集做修改或擴充,所以還是有一點差異。使得程式語言所使用的編解碼器(Codec)還是有區別。Code Page 的 Codec 通常以 cpXXX 表示,例如中文使用 cp950。
  2. 嚴格來講,0 跟 1 的序列。
  3. 例如:「繧ク繝ァ繝」字串,就是 UTF-8 的文件用 Shift-JIS 讀取造成。
    又例如:日本 Windows 檔案路徑反斜線 \ 都顯示成日幣符號 ¥,詳情可搜尋:円記号問題。
  4. 每個字的長相也有編索引(Character ID, CID),詳細規定了各種異體字的長相。字型製作時,則使用字符索引值(Glyph ID,最大值即為實作字數),即內嵌的 cmap 去映射對應支援文字編碼(以固定長度編碼為主)。異體字選擇器功能則可以透過 UVS Table 實現。