關於編碼之一:Unicode/UTF-8/UTF-16/UTF-32
1.關於編碼,繞不開下面這些概念
①Unicode/UTF-8/UTF-16/UTF-32
②大小端字節序(big-endian/little-endian)
③BOM(Byte Order Mark)
2.關於Unicode/UTF-8/UTF-16/UTF-32
①Unicode其實應該是一個碼值表。(百度百科:Unicode的功用是為每一個字符提供一個唯一的代碼(即一組數字))。
②UTF-8/UTF-16/UTF-32是通過對Unicode碼值進行對應規則轉換後,編碼保持到內存/文件中。UTF-8/UTF-16/UTF-32都是可變長度的編碼方式。(後面將進行Unicode碼值轉換為UTF-8的說明)。
③我們平常說的 “Unicode編碼是2個字節” 這句話,其實是因為windows默認的Unicode編碼就是UTF-16,在常用基本字符上2個字節的編碼方式已經夠用導致的誤解,其實是可變長度的。
在沒有特殊說明的情況下,常說的Unicode編碼可以理解為UTF-16編碼。
④UTF-32是因為UTF-16編碼方式不能表示全部的字符而擴充的編碼方式。
ps:顯示的字符是表現形式,具體內存中的編碼方式和字符顯示之間通過中間層進行轉換。(根據編碼規則,1個字符可能對應內存中1個到幾個字節。)
3.UTF-8編碼
①UTF編碼方式,按照規則轉換後,第1個字節仍與ASCII兼容,這使得原來處理ASCII字符的軟件無須或只須做少部分修改,即可繼續使用。
網絡上數據傳輸英文字符只需要1個字節,可以節省帶寬資源。當前大部分的網絡應用都使用UTF-8編碼。(中文按照規則會轉換為3個字節,反而浪費資源,沒辦法,規則別人定好了!)
②UTF-8編碼需要進行字節數轉換+補碼兩個步驟
Unicode碼值轉UTF-8編碼規則之一(字節數轉換)
•1個字節:Unicode碼為0 - 127
•2個字節:Unicode碼為128 - 2047
•3個字節:Unicode碼為2048 - 0xFFFF
•4個字節:Unicode碼為65536 - 0x1FFFFF
•5個字節:Unicode碼為0x200000 - 0x3FFFFFF
•6個字節:Unicode碼為0x4000000 - 0x7FFFFFFF
Unicode碼值轉UTF-8編碼規則之二(二進制補碼)
對應上面規則一字節數轉換後,具體的補位碼如下,"x"表示空位,用來補位的,補不全則使用0。
可以看到規律,第一個字節前面有多少個1就表示占用多少個字節,紅色顏色位固定不變。(0-127的值直接使用0表示占用1個字節)
•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編碼字符A
•字節數轉換:字符A的Unicode碼值為65,位於0-127的區間,所以占1個字節,其二進制值為1000001(7位)
•補碼:使用1個字節0xxxxxxx格式進行補碼(7個x),將上面的7位二進制值從右到左填到7個x中,得到01000001(8位)
得到字符A的UTF-8編碼為01000001(8位)
④使用UTF-8編碼中文字符“中”
•字節數轉換:中文字符“中”的Unicode碼值為20013,位於2048-0xFFFF的區間,所以占3個字節,其二進制值為1001110 00101101(15位)
•補碼:使用3個字節1110xxxx 10xxxxxx 10xxxxxx格式進行補碼(16個x),將上面的15位二進制值從右到左填到16個x中(不足位則將x變為0),得到11100100 10111000 10101101
得到中文字符“中的”UTF-8編碼位11100100 10111000 10101101(24位)
4.大小端字節序(big-endian/little-endian)
①字節序表示在內存/文件中字節的保存順序,由於硬件讀寫順序的不同,導致出現了大端和小端兩種方式。
大端:數據的高字節保存在內存的低地址中,低字節保存到內存的地址中,和我們的閱讀習慣一致;小端則相反,常用的X86結構是小端模式。
采用大端方式進行數據存放符合人類的正常思維,而采用小端方式進行數據存放利於計算機處理。
②UTF-8編碼不存在字節序大小端問題!(因為字節序只影響同時處理多於兩個字節的編碼方式,比如UTF-16/UTF-32,而UTF-8是按照單字節進行處理的)
UTF-8的解碼都必須先讀取首字節獲取字節數,所以必須找到首字節的第一位要麽是0,要麽是110/1110/11110/111110/1111110,所以上面的“中”字,無論是保存為11100100 10111000 10101101還是10101101 10111000 11100100,都必須要先找到11100100這個字節,所以UTF-8從機制上就能避免字節序的問題。
③UTF-16/UTF-32存在字節序問題(UTF-16常用情況下一次處理2個字節/UTF-32一次處理4個字節)!一個“奎”的Unicode碼值是0x594E,“乙”的Unicode碼值是0x4E59。如果我們的UTF-16字節數據是0x594E,那麽這是“奎”還是“乙”?如果大端序,0x594E是“奎”,如果是小端序,0x4E59,是“乙”。
5.BOM
①為了保證編碼和解碼字節順序問題(因為只有保證編碼和解碼的規則一致才能保證是同一個字符),所以Unicode規範中推薦的標記字節順序的方法是BOM(Byte Order Mark)。
②UTF-8不需要BOM來表明字節順序,但可以用BOM來表明編碼方式。根據BOM的規則,在一段字節流開始時,如果接收到以下字節,則分別表明了該文本文件的編碼。
UTF-8: EF BB BF
UTF-16 : FF FE
UTF-16 big-endian: FE FF
UTF-32 little-endian: FF FE 00 00
UTF-32 big-endian: 00 00 FE FF
而如果不是以這個開頭,那程序則會以ANSI,也就是系統默認編碼讀取。
如同樣是字符“A”﹐在以下幾種格式中的存儲形式分別是﹕
UTF-16 big-endian : 00 41
UTF-16 little-endian : 41 00
UTF-32 big-endian : 00 00 00 41
UTF-32 little-endian : 41 00 00 00
參考資料:
《為什麽utf-8沒有字節序問題?》
《UTF的字節序和BOM》
關於編碼之一:Unicode/UTF-8/UTF-16/UTF-32