目錄
如下圖所示,在記事本里輸入"編碼",然后另存為的時候,有四種編碼:
圖1
按下表所示,四種編碼存為四個文件:
編碼 |
文件名 |
ANSI |
A.txt |
Unicode |
U.txt |
Unicode big endian |
UB.txt |
UTF-8 |
U8.txt |
使用VC++6.0或Visual Studio以二進制方式打開這四個文件。二進制編碼一目了然,如下圖所示:
圖2
1 ANSI編碼
A.txt有四個字節:B1 E0 C2 EB。其中B1 E0是"編"的GBK編碼,C2 EB是"碼"的GBK編碼。
所以,記事本里的ANSI編碼,對於簡體中文操作系統而言,就是GBK編碼。對於繁體中文操作系統而言,就是Big5編碼……
2 UTF16BE編碼
UB.txt有六個字節:FE FF 7F 16 78 01。其中FE FF是BOM(Byte Order Mark),暫時不用管它。0x7F16是"編"的Unicode編碼,0x7801是"碼"的Unicode編碼。
UTF16BE編碼是16位(2字節)的Unicode編碼,BE表示big endian,即高位字節在前,低位字節在后。Unicode編碼0x7F16的高位字節是7F,低位字節是16,UTF16BE編碼就是7F 16。
3 UTF16LE編碼
U.txt有六個字節:FF FE 16 7F 01 78。其中FF FE是BOM,暫時不用管它。0x7F16是"編"的Unicode編碼,0x7801是"碼"的Unicode編碼。
UTF16LE編碼是16位(2字節)的Unicode編碼,LE表示little endian,即低位字節在前,高位字節在后。Unicode編碼0x7F16的高位字節是7F,低位字節是16,UTF16LE編碼就是16 7F。
可見:UTF16LE與UTF16BE只是高低位字節交換了一下而已。
4 UTF-8編碼
U8.txt有九個字節:EF BB BF E7 BC 96 E7 A0 81。其中EF BB BF是BOM,暫時不用管它。E7 BC 96是"編"的UTF-8編碼,E7 A0 81是"碼"的UTF-8編碼。
5 BOM
BOM是Byte Order Mark的縮寫,它用來指明編碼,如下所示:
BOM |
編碼 |
FE FF |
UTF16BE |
FF FE |
UTF16LE |
EF BB BF |
UTF-8 |
上面的FE FF和FF FE正好逆序,這也就是Byte Order Mark(字節順序標記)的來由吧。
6 亂碼
記事本通過BOM來區分各種編碼,為什么不給ANSI搞個BOM?原因在於——向下兼容。從DOS到Win98,文本文件都是ANSI編碼,都沒有BOM。為了能夠順利的打開這些文件,不能增加BOM。
通過BOM來區分各種編碼,是一個非常好的想法。不過,沒有歷史包袱的Linux不買賬——Linux默認就使用UTF-8編碼,而且是沒有BOM的UTF-8編碼。
為了能夠打開Linux生成的沒有BOM的UTF-8編碼文件,記事本在打開沒有BOM的文本文件時,會對其進行檢查。如果所有編碼符合UTF-8,就以UTF-8編碼打開。
把圖1中的"編碼"替換為"聯通",另存為ANSI編碼。再次打開,顯示如下圖所示:
圖3
使用VC++6.0打開這個文件,一切正常,如下圖所示:
圖4
記事本顯示亂碼,是因為它會把"聯通"的GBK編碼C1 AA CD A8當做UTF-8編碼進行顯示;VC++6.0沒有顯示亂碼,是因為它不支持UTF-8編碼,只支持ANSI編碼。
有哪些漢字的GBK碼會被當做UTF-8編碼呢?一段MFC代碼就讓它們原形畢露了:
CFile f; if(f.Open(_T("W:\\1.txt"),CFile::modeCreate | CFile::modeWrite)) { f.Write(":\r\n",4); //這句很重要,否則記事本打開后顯示亂碼 int q = 0; //區碼 int w = 0; //位碼 int c = 0; BYTE n[2]; //內碼 for(q = 0x81;q <= 0xFE;++q) { n[0] = q; for(w = 0x40;w <= 0xFE;++w) { n[1] = w; if(n[0] >= 0xC0 && n[0] <= 0xDF && n[1] >= 0x80 && n[1] <= 0xBF) { f.Write(n,2); if(++c >= 40) { c = 0; f.Write("\r\n",2); } } } } f.Close(); } |
運行結果如下:
圖5
這樣的漢字竟然有2048個。除了"聯通"還有如下常見的漢字:
乾坤、學習、史實、母女、孝順、魯莽、矛盾、沉默、詩詞、腳趾、拇指、農忙、投石、泰山、水簾、矢量、糧食、太平、謙遜、堯舜、一十百千 |
注意:上圖第一行的全角冒號很重要,就是因為它的存在,記事本才不會誤判編碼為UTF-8,也就不會亂碼顯示了。類似的字符還有很多,如下所示:
,、:;""。!……——【】■□▲△◆◇○◎●★☆←↑→↓ |
7 總結
Windows下,文本文件有五種編碼:ANSI、UTF16BE、UTF16LE、UTF-8有BOM、UTF-8無BOM(僅讀取時支持該編碼)。
另存為ANSI編碼時,因為沒有BOM,所以有可能會被記事本、UltraEdit等文本編輯器當做無BOM的UTF-8編碼,導致顯示亂碼。
生成的文本文件除非要用於Win98,否則最好使用UTF-8編碼保存。
某些軟件,如:Android Studio強制要求源代碼文件使用無BOM的UTF-8編碼。對於這類文件,可使用記事本查看,不要保存。否則前面三個字節的BOM(EF BB BF)刪除起來還是比較麻煩的。
為了方便的在這五種編碼之間相互轉換,可參考筆者的博文:
http://blog.csdn.net/hanford/article/details/53351153