字符集&各類編碼&編碼解碼

要理解亂碼問題,首先須要理解幾個概念:字符集、編碼、編碼規則、亂碼安全

1. 字符集:測試

字符(Character)是各類文字和符號的總稱,包括各國家文字、標點符號、圖形符號、數字等。字符集(Character set)是多個字符的集合,字符集種類較多,每一個字符集包含的字符個數不一樣,常見字符集名稱:ASCII字符集、GB2312字符集、BIG5字符集、 GB18030字符集、Unicode字符集等。其實字符集簡單了來講,就是一張表格,是 id 和字符的對應表。編碼

2. 各類編碼:代理

一種編碼格式必須選定一個字符集。好比 UTF-8和 UTF-16 / UTF-32 選用 Unicode 字符集,GB2312選用GB2312字符集字符集。code

3. 不一樣字符集的編碼、解碼規則:同步

(1)UTF-8的編碼規則:it

UTF-8是一種變長字節編碼方式。對於某一個字符的UTF-8編碼,若是隻有一個字節則其最高二進制位爲0;若是是多字節,其第一個字節從最高位開始,連續的二進制位值爲1的個數決定了其編碼的位數,其他各字節均以10開頭。UTF-8最多可用到6個字節。 
如表: 
1字節 0xxxxxxx 
2字節 110xxxxx 10xxxxxx 
3字節 1110xxxx 10xxxxxx 10xxxxxx 
4字節 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 
5字節 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 
6字節 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 
所以UTF-8中能夠用來表示字符編碼的實際位數最多有31位,即上表中x所表示的位。除去那些控制位(每字節開頭的10等),這些x表示的位與UNICODE編碼是一一對應的,位高低順序也相同。 
實際將UNICODE轉換爲UTF-8編碼時應先去除高位0,而後根據所剩編碼的位數決定所需最小的UTF-8編碼位數。 
所以那些基本ASCII字符集中的字符(UNICODE兼容ASCII)只須要一個字節的UTF-8編碼(7個二進制位)即可以表示。字符編碼

(2)UTF-16的編碼規則:亂碼

UTF-16是Unicode字符集的一種轉換方式,即把Unicode的碼位轉換爲16比特長的碼元串行,以用於數據存儲或傳遞。軟件

2.2.1 從U+D800到U+DFFF的碼位(代理區)

由於Unicode字符集的編碼值範圍爲0-0x10FFFF,而大於等於0x10000的輔助平面區的編碼值沒法用2個字節來表示,因此Unicode標準規定:基本多語言平面內,U+D800..U+DFFF的值不對應於任何字符,爲代理區。所以,UTF-16利用保留下來的0xD800-0xDFFF區段的碼位來對輔助平面的字符的碼位進行編碼。

可是在使用UCS-2的時代,U+D800..U+DFFF內的值被佔用,用於某些字符的映射。但只要不構成代理對,許多UTF-16編碼解碼仍是能把這些不符合Unicode標準的字符映射正確的辨識、轉換成合規的碼元. 按照Unicode標準,這種碼元串行原本應算做編碼錯誤.


2.2.2 從U+0000至U+D7FF以及從U+E000至U+FFFF的碼位

第一個Unicode平面(BMP),碼位從U+0000至U+FFFF(除去代理區),包含了最經常使用的字符。UTF-16與UCS-2編碼在這個範圍內的碼位爲單個16比特長的碼元,數值等價於對應的碼位。BMP中的這些碼位是僅有的碼位能夠在UCS-2被表示。


2.2.3 從U+10000到U+10FFFF的碼位

輔助平面(Supplementary Planes)中的碼位,大於等於0x10000,在UTF-16中被編碼爲一對16比特長的碼元(即32bit,4Bytes),稱做 code units called a 代理對(surrogate pair),具體方法是:

Ø 碼位減去0x10000, 獲得的值的範圍爲20比特長的0..0xFFFFF(由於Unicode的最大碼位是0x10ffff,減去0x10000後,獲得的最大值是0xfffff,因此確定能夠用20個二進制位表示),寫成二進制形式:yyyy yyyy yyxx xxxx xxxx。

Ø 高位的10比特的值(值的範圍爲0..0x3FF)被加上0xD800獲得第一個碼元或稱做高位代理(high surrogate), 值的範圍是0xD800..0xDBFF。因爲高位代理比低位代理的值要小,因此爲了不混淆使用,Unicode標準如今稱高位代理爲前導代理(lead surrogates)。

Ø 低位的10比特的值(值的範圍也是0..0x3FF)被加上0xDC00獲得第二個碼元或稱做低位代理(low surrogate), 如今值的範圍是0xDC00..0xDFFF。 因爲低位代理比高位代理的值要大,因此爲了不混淆使用,Unicode標準如今稱低位代理爲後尾代理(trail surrogates)。

Ø 最終的UTF-16(4字節)的編碼(二進制)就是:110110yyyyyyyyyy 110111xxxxxxxxxx。


按照上述規則,Unicode編碼0x10000-0x10FFFF的UTF-16編碼有兩個WORD,第一個WORD的高6位是110110,第二個WORD的高6位是110111。可見,第一個WORD的取值範圍(二進制)是11011000 00000000到11011011 11111111,即0xD800-0xDBFF。第二個WORD的取值範圍(二進制)是11011100 00000000到11011111 11111111,即0xDC00-0xDFFF。上面所說的從U+D800到U+DFFF的碼位(代理區),就是爲了將一個WORD(2字節)的UTF-16編碼與兩個WORD的UTF-16編碼區分開來。

因爲高位代理、低位代理、BMP中的有效字符的碼位,三者互不重疊,搜索是簡單的: 一個字符編碼的一部分不可能與另外一個字符編碼的不一樣部分相重疊。這意味着UTF-16是自同步(self-synchronizing):能夠經過僅檢查一個碼元就能夠斷定給定字符的下一個字符的起始碼元。 UTF-8也有相似優勢,但許多早期的編碼模式就不是這樣,必須從頭開始分析文本才能肯定不一樣字符的碼元的邊界。

因爲最常有的字符都在基本多文種平面中,許多軟件的處理代理對的部分每每得不到充分的測試。這致使了一些長期的bug與潛在安全漏洞,甚至在廣爲流行獲得良好評價的應用軟件。

(3)UTF-32的編碼規則:

UTF-32編碼以32位無符號整數爲單位。Unicode的UTF-32編碼就是其對應的 32位無符號整數。
字節序
根據字節序的不一樣,UTF-16能夠被實現爲UTF-16LE或UTF-16BE,UTF- 32能夠被實現爲UTF-32LE或UTF-32BE。例如:
Unicode編碼 ║ UTF-16LE ║ UTF-16BE ║ UTF32-LE ║  UTF32-BE 
0x006C49 ║ 49 6C ║ 6C 49 ║ 49 6C 00 00 ║ 00 00 6C 49 
0x020C30 ║ 43 D8 30 DC ║ D8 43 DC 30 ║ 30 0C 02 00 ║ 00 02 0C 30 
那麼,怎麼判斷字節流的字節序呢?Unicode標準建議用BOM(Byte Order Mark)來區分字節序,即在傳輸字節流前,先傳輸被做爲BOM的字符"零寬無中斷空格"。這個字符的編碼是FEFF,而反過來的FFFE(UTF- 16)和FFFE0000(UTF-32)在Unicode中都是未定義的碼位,不該該出如今實際傳輸中。下表是各類UTF編碼的BOM:
UTF編碼 ║ Byte Order Mark 
UTF-8 ║ EF BB BF 
UTF-16LE ║ FF FE 
UTF-16BE ║ FE FF 
UTF-32LE ║ FF FE 00 00 
UTF-32BE ║ 00 00 FE FF

 

上述三種編碼方式各有優劣:

其中 UTF-8是變長的,最節省空間的,UTF-32是空間開銷最大的。UTF-16空間開銷折中,可是有個缺點就是缺乏某些字符的編碼。

 

4.亂碼:

爲何會出現亂碼呢?有兩種可能的緣由,一種是選擇了錯誤的編碼,一種是選擇了錯誤的解碼。

可是有的亂碼是可逆的,有的是不可逆的。

用 ASCII 進行解碼是可逆的。由於ASCII 的256個字符集,均可以用8位二進制數表示。而像 UTF-16這種的,有的二進制表示是沒有對應的字符集的,找不到,就是不可逆的。能在字符集找到的就是可逆的。

相關文章
相關標籤/搜索