UTF8、UTF16、UTF16-LE、UTF16-BE、UTF32都是些什麼?
下述內容大部分引用自CSDN:
Unicode 是 unicode.org 制定的編碼標準,目前得到了絕大部分作業系統和程式語言的支援。unicode.org 官方對 Unicode 的定義是:Unicode provides a unique number for every character, no matter what the platform, no matter what the program, no matter what the language。可見,Unicode 所做的是為每個字元定義了一個相應的數字表示。比如,“a”的 Unicode 值是 0x0061,“一”的 Unicode 值是 0x4E00,這是最簡單的情況,每個字元用2個位元組表示。
unicode.org 定義了百萬個以上的字元,如果將所有的字元用統一的格式表示,需要的是 4 個位元組。“a”的 Unicode 表示就會變成 0x00000061,而“一“的 Unicode 值是 0x00004E00。實際上,這就是 UTF32,Linux 作業系統上所使用的 Unicode 方案。
但是,仔細分析可以發現,其實絕大部分字元只使用 2 個位元組就可以表示了。英文的 Unicode 範圍是 0x0000-0x007F,中文的 Unicode 範圍是 0x4E00-0x9F**,真正需要擴充套件到 4 個位元組來表示的字元少之又少,所以有些系統直接使用 2 個位元組來表示 Unicode。比如 Windows 系統上,Unicode 就是兩個位元組的。對於那些需要 4 個位元組才能表示的字元,使用一種代理的手法來擴充套件(其實就是在低兩個位元組上做一個標記,表示這是一個代理,需要連線上隨後的兩個位元組,才能組成一個字元)。這樣的好處是大量的節約了存取空間,也提高了處理的速度。這種 Unicode 表示方法就是 UTF16。一般在 Windows 平臺上,提到 Unicode,那就是指 UTF16 了。
至於 UTF16-LE 和 UTF16-BE,則與計算機的 CPU 構架有關。LE 指 Little Endian,而 BE 指 Big Endian。由於 UTF16 是雙位元組編碼,所以兩個位元組儲存時哪個在前,哪個在後關係到解析出字元的結果。至於為什麼會出現 BE 和 LE 的編碼,則是由於歷史原因造成的:在 Mac 和 PC 機上,對位元組順序的理解是不一致的。如果一個檔案不明確說明 UTF16 使用的是 BE 還是 LE,那麼就需要通過 BOM 來指明瞭。我們一般的 X86 系統都是 Little Endian 的,可以認為 UTF16=UTF16-LE。
由於對於歐洲和北美,實際上使用的編碼範圍在 0x0000-0x00FF 之間,只需要一個字元就可以表示所有的字元。即使是使用 UTF16 來作為記憶體的存取方式,還是會帶來巨大的空間浪費,因此就有了 UTF8 的編碼方式。UTF8 是一個可變長度字元編碼,它同時是一個字首碼,字首碼的特徵是,編碼系統中的任意一個合法的碼不會是另外一個碼的字首,所以 UTF8 不需要指定位元組序。一個 UTF8 編碼可以用 1~6 個位元組來表示,將第一個位元組的前幾個位元設定為 1 來指定這個字元佔用幾個位元,比如一個兩位元組的字元的編碼,第一位是 110xxxxx,第二位是 10xxxxxx,而一個六位元組字元的編碼是這樣的:1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx,所以 UTF-8 最多能編碼 231 個字元。所以對於只需要1個位元組的字元,就使用一個位元組。對於中日韓等原本需要兩個位元組才能表示的字元,則通過一個UTF16-UTF8 的演算法實現相互之間的轉換,一般需要 3 個位元組才能表示。UTF8 使用的演算法很有意思,大致對映關係如下:
Unicode編碼 | UTF-8編碼(二進位制) |
U+0000 – U+007F | 0xxxxxxx |
U+0080 – U+07FF | 110xxxxx 10xxxxxx |
U+0800 – U+FFFF | 1110xxxx 10xxxxxx 10xxxxxx |
U+10000 – U+10FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
由於 UTF8 可以方便的轉換為 UTF16 和 UTF32,而且 UTF8 在每個作業系統平臺上的實現都是一樣的,也不存在跨平臺的問題,所以 UTF8 成為跨平臺的 Unicode 很好的解決方案。當然,對於中文來說,由於每個字元需要 3 個位元組才能表示,還是有點浪費的。