淺談 Unicode、UTF-8、UTF-16 和 UTF-32
電腦其實只以儲存和處理數字,而電腦的數字是以二進制的,亦即只有 1 和 0,要電腦處理文字,首先要為文字編上一個數字。
而 Unicode 就是為所有字元都編上一個獨有的數字,由於是獨一的數字,同一編碼就只會對應一個字元,這就可解決亂碼的問題。之於 UTF-8、UTF-16 和 UTF-32 就是如何儲存 Unicode 編碼的方法,UTF 全名是 Unicode Transformation Format。
問題是全世界有這麼多字元,數字要多大才夠?Unicode 1.0 以為 2 bytes﹝即是 16-bit 就足以處理,故此出現了 UTF-16 / UCS-2﹞。但好快就發覺 2 bytes 不足以為所有字元編碼,故此就出現了 Unicode 2.0,它是以 21 bits 編碼。目前 Unicode 最新標準是 Unicode 6.0,21 bits 空間仍然十分充裕。
UTF-16 原意是以固定 2 bytes 儲存一個字元,這樣運算和處理起來都十分方便有效率,無奈 2 bytes 無法為所有字元編碼。UTF-16 的做法是如果 FFFF 編碼內的就以 2 bytes 表示,FFFF 以上就通過 BMP (Basic Multilingual Plane) 以 4 bytes 表示。
在 FFFF 編碼內的字元,例如英文字母,UTF-16 是以固定 2 bytes 編碼。例如 a 字的 Unicode 編碼是 61。如果以 UTF-16 表示就是 00 61。而用 ASCII 則是 61。由此可見 UTF-16 是和 ASCII 和 ISO-8859-1 不相容。
順帶一題,ASCII 是以 7-bit 編碼,而 ISO-8859-1 是以 8-bit 編碼,它以 ASCII 為基礎,用餘下的在空置的 0xA0-0xFF 的範圍內,加入 96 個字母及符號。
為解決 UTF-16 有時 2 bytes 表示,有時 4 bytes 的問題,就出現了 UTF-32 / USC-4。雖然 Unicode 只用上 21 bit,但電腦處理的位元數多以 4 的倍數上,即是 4 bit、8 bit、16 bit、32 bit、64 bit 等。所以 UTF-32 是固定以 32 bit 編碼。
UTF-16 雖然同 ASCII 不相容,但作為內部編碼則沒有這問題,所以 UTF-16 被廣泛用於程式的內部編碼,如 Java。雖然 UTF-32 有固定編碼的好處,但由於太浪費記憶體,加上需要 BMP 轉換的情況不多,也由於歷史因素,例如當年 Java 面世時,Unicode 還是以 16 bit 編碼。所以 UTF-32 現在還未算普及。
而作為外部編碼﹝即是用作交換資息﹞UTF-8 是首選。UTF-8 的編碼長度是可變的,1 byte 至 6 bytes 都有可能,對於 ASCII 碼範圍內的字元,UTF-8 的表示方式跟 ASCII 是一樣的,都是使用 1 byte。相容 ASCII 加上 UTF-8 沒有大小尾序﹝Big / Little Endian﹞ 使 UTF-8 作為 Unicode 外部編碼的不二之選。
UTF-8 不使用大尾序和小尾序。除了第一個 byte 外,其餘每個 byte 使用 10 開始,第 1 byte 是用來表示這個字元會用上幾上個 bytes。例如 110 即是這個字元會用上 2 個 bytes。
1 byte 的時候:0xxxxxxx
2 bytes 的時候:110xxxxx 10xxxxxx
3 bytes 的時候:1110xxxx 10xxxxxx 10xxxxxx
4 bytes 的時候:11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
當 UTF-8 用 4 bytes 儲存字元時,有 3 + (8 – 2) x 3 = 21 bits 編碼空間,足夠儲存所有 Unicode 編碼。
以下我就以「謝」字講解 UTF-8 編碼的方法:
透過「字元對應表」或 Hex Editor ﹝以 UTF-16 編碼編輯﹞得知「謝」字的 Unicode 編碼是 8B1D,8B1D 是 16 進制,換成 2 進制就是 1000101100011101。
套上 UTF-8 的形式:
(1110)1000 (10)101100 (10)011101
() 內的數字是因應 UTF-8 的要求加上去的,第 1 byte 的 1110 就表示了這個字元需要 3 個 bytes。
換成 16 進制,「謝」字的 UTF-8 編碼就是 E8 AC 9D。