GB2312、Unicode編碼等


  • 拋出問題:

我在CPP文件中,打算輸出一行阿拉伯字符:

當試圖運行時,會彈出以下提示:

即便點擊“是”,運行之后也是顯示不出來的:

為什么會出現這種情況?我們先拿txt文件做個解釋:

如果你在txt文件中輸入阿拉伯文

另存為ANSI格式后,再打開是這樣的:

但如果你另存為utf-8編碼格式,就能正確顯示:

VS中的cpp文件、.h文件也是同樣的道理,這里顯然是保存格式的問題,我們回到VS。

通過高級保存選項:

可以看到cpp文件的編碼格式:

或者

這樣的編碼格式設置。

  • GB2312:

介紹下GB2312是信息交換漢字編碼字符集,適用於漢字處理、漢字通信等系統之間的信息交換,通行於中國大陸。

GB 2312中對所收漢字進行了“分區”處理,每區含有94個漢字/符號。這種表示方式也稱為 區位碼
01-09區為特殊符號。
16-55區為一級漢字,按拼音排序。
56-87區為二級漢字,按部首/筆畫排序。
10-15區及88-94區則未有編碼。
舉例來說,“啊”字是GB2312之中的第一個漢字,它的區位碼就是1601。
每個漢字及符號以兩個字節來表示。
例如:
輸出:
第一個 字節稱為“高位字節”(也稱“區字節)”,第二個字節稱為“低位字節”(也稱“位字節”)。
 
“高位字節”使用了0xA1-0xF7(把01-87區的區號加上0xA0),“低位字節”使用了 0xA1-0xFE(把01-94加上 0xA0)。 由於一級漢字從16區起始,漢字區的“高位字節”的范圍是0xB0-0xF7,“低位字節”的范圍是0xA1-0xFE,占用的碼位是 72*94=6768。其中有5個空位是D7FA-D7FE。
例如“啊”字在大多數程序中,會以兩個 字節,0xB0(第一個字節) 0xA1(第二個字節)儲存。 區位碼=區字節+位字節(與區位碼對比:0xB0=0xA0+16,0xA1=0xA0+1)。
輸出:

如果強制使用ASNI編碼輸出則會輸出亂碼:

如果按照Shift—JIS編碼,會輸出:

我們改為一段日文進行嘗試:

輸出結果就是正確的:

當我們把編碼格式轉變為朝鮮語時:

日文就顯示不出來,但是其16進制編碼還是可以顯示出來的:

我們發現盡管日文沒有顯示出來,但是其編碼是一樣的。

其實不論編碼格式是怎樣的,字符在內存當中的存在都是以固定的二進制形式存在的,只是你用不同的編碼方式去解析它,它被不同的解析方式解析之后,顯示出來的字符就不同。比如你用GB2312編碼格式去解析日文編碼格式,就能成功解析日文,原因是GB2312編碼中涵蓋了這些日文。再比如你用阿拉伯語言編碼格式去解析這三個日文字符就解析成這樣:

原因就在於雖然在內存中的存在是aaef、aabf、aab7這樣的十六進制形式,但有可能你在使用的這個阿拉伯文編碼格式中的阿拉伯字母編碼都在3f以下,所以你只能解析3f以下的,這里的aaef、aabf、aab7顯然超過了3f就被阿拉伯編碼按照最大的3f進行解析了。

  • Unicode編碼:

 下面再來說Unicode編碼。從ASCII、GB2312、GBK到GB18030的編碼方法是向下兼容的。而Unicode只與ASCII兼容(更准確地說,是與ISO-8859-1兼容),與GB碼不兼容。例如“漢”字的Unicode編碼是6C49,而GB碼是BABA。前邊的GB2312可以顯示中文、日文等字符,但是對於阿拉伯文等字符就無能為力了,這里就要依靠Unicode編碼。Unicode又叫做統一碼或者萬國碼是一種在計算機上使用的字符編碼,它為每種語言中的每個字符設定了統一並且唯一的二進制編碼,以滿足跨語言、跨平台進行文本轉換、處理的要求。Unicode用數字0-0x10FFFF來映射這些字符,最多可以容納1114112個字符,或者說有1114112個碼位。碼位就是可以分配給字符的數字。UTF-8、UTF-16、UTF-32都是將數字轉換到程序數據的編碼方案。在Unicode中:例如,漢字“字”對應的數字是23383,我們有很多方式將數字23383表示成程序中的數據,包 括:UTF-8、UTF-16、UTF-32。

例如,“漢字”對應的數字是 0x6c49和0x5b57,而編碼的程序數據是:

BYTE data_utf8[] = {0xE6, 0xB1, 0x89, 0xE5, 0xAD, 0x97}; // UTF-8編碼
 WORD data_utf16[] = {0x6c49, 0x5b57}; // UTF-16編碼
DWORD data_utf32[] = {0x6c49, 0x5b57}; // UTF-32編碼

這里用BYTE、WORD、DWORD分別表示無符號8位整數,無符號16位整數和無符號32位整數。UTF-8、UTF-16、UTF- 32分別以BYTE、WORD、DWORD作為編碼單位。“漢字”的UTF-8編碼需要6個字節。“漢字”的UTF-16編碼需要兩個WORD,大小是4 個字節。“漢字”的UTF-32編碼需要兩個DWORD,大小是8個字節。根據字節序的不同,UTF-16可以被實現為UTF-16LE或UTF- 16BE,UTF-32可以被實現為UTF-32LE或UTF-32BE。

 這里說下Unicode編碼和UTF-8的關系,Unicode是一個字符集,而UTF-8是Unicode的其中一種,Unicode是定長的都為雙字節,而UTF-8是可變的,對於漢字來說Unicode占有的字節比UTF-8占用的字節少1個字節。Unicode為雙字節,而UTF-8中漢字占三個字節。

Unicode編碼和UTF-8編碼的對應關系如下:

 

  • UTF-8:

UTF-8的特點是對不同范圍的字符使用不同長度的編碼。對於0×00-0x7F之間的字符,UTF-8編碼與ASCII編碼完全相同。 UTF-8編碼的最大長度是4個字節。從上表可以看出,4字節模板有21個x,即可以容納21位二進制數字。Unicode的最大碼位0x10FFFF也只有21位。總結了一下規律:UTF-8的第一個字節開始的1的個數代表了總的編碼字節數,后續字節都是以10開始。由上面的規則可以清晰的看出UTF-8編碼克服了中文編碼的兩個問題。

    例1:“漢”字的Unicode編碼是0x6C49。0x6C49在0×0800-0xFFFF之間,使用3字節模板了:1110xxxx 10xxxxxx 10xxxxxx。將0x6C49寫成二進制是:0110 1100 0100 1001, 用這個比特流依次代替模板中的x,得到:11100110 10110001 10001001,即E6 B1 89。

    例2:Unicode編碼0x20C30在0×010000-0x10FFFF之間,使用用4字節模板了:11110xxx 10xxxxxx 10xxxxxx 10xxxxxx。將0x20C30寫成21位二進制數字(不足21位就在前面補0):0 0010 0000 1100 0011 0000,用這個比特流依次代替模板中的x,得到:11110000 10100000 10110000 10110000,即F0 A0 B0 B0。

  • UTF-16

    UTF-16編碼以16位無符號整數為單位。我們把Unicode編碼記作U。編碼規則如下:如果U<0×10000,U的 UTF-16編碼就是U對應的16位無符號整數(為書寫簡便,下文將16位無符號整數記作WORD)。中文范圍 4E00-9FBF,所以在UTF-16編碼里中文2個字節編碼。如果U≥0×10000,我們先計算U’=U-0×10000,然后將U’寫成二進制形 式:yyyy yyyy yyxx xxxx xxxx,U的UTF-16編碼(二進制)就是:110110yyyyyyyyyy 110111xxxxxxxxxx。

  • UTF-32

    UTF-32編碼以32位無符號整數為單位。Unicode的UTF-32編碼就是其對應的32位無符號整數。

  • 字節序和BOM

UTF-8以字節為編碼單元,沒有字節序的問題。UTF-16以兩個字節為編碼單元,在解釋一個UTF-16文本前,首先要弄清楚每個編碼單元的字節序。 例如“奎”的Unicode編碼是594E,“乙”的Unicode編碼是4E59。如果我們收到UTF-16字節流“594E”,那么這是“奎”還是 “乙”?
Unicode規范中推薦的標記字節順序的方法是BOM。BOM是Byte order Mark。在 UCS編碼中有一個叫做"ZERO WIDTH NO-BREAK SPACE"的字符,它的編碼是FEFF。而FFFE在UCS中是不存在的字符,所以 不應該出現在實際傳輸中。UCS規范建議我們在傳輸字節流前,先傳輸字符"ZERO WIDTH NO-BREAK SPACE"。這樣如果接收者收到FEFF,就表明這個字節流是Big-Endian的;如果收到FFFE,就表明這個字節流是Little-Endian的。因此字符"ZERO WIDTH NO-BREAK SPACE"又被稱作BOM。UTF- 8不需要BOM來表明字節順序,但可以用BOM來表明編碼方式。字符"ZERO WIDTH NO-BREAK SPACE"的UTF-8編碼是 EF BB BF(讀者可以用我們前面介紹的編碼方法驗證一下)。所以如果接收者收到以EF BB BF開頭的字節流,就知道這是UTF-8編碼了。

 

參考:

http://www.dreamdu.com/blog/2008/08/02/character_encoding/

這篇文章寫得很詳細

http://www.cnblogs.com/cy163/archive/2007/05/31/766886.html

這篇《中文編碼雜談》寫得不錯,里邊還有UTF-8判斷以及編碼轉換的C++實現,推薦大家看下

http://www.cnblogs.com/xkfz007/articles/2566434.html

這篇文章中也有判斷是否是UTF-8編碼的源碼:

http://m.blog.csdn.net/blog/zison_sun/5815746

但是當你的文本如果原本就只有英文字符,那么即使你的文本是GB2312,也會被判別為是UTF-8.因為GB2312中的英文字符和UTF-8中英文字符原本就一樣,如果你的文中全是英文字符,這個代碼會認為所有的字符都是UTF-8中屬於范圍00000000-0000007F中的字符。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM