詳談字符編碼[二]代碼頁和一個亂碼案例

        上一篇關於字符編碼的隨筆介紹了編碼,輸入碼,機內碼,字形碼,字形庫等概念。除此以外,還有一些其餘的概念咱們不得不瞭解,它們已經不屬於如今,可是卻時常影響着如今。代碼頁,正是這些有歷史感的概念之一。這篇博文帶你瞭解代碼頁和當前Windows對Unicode和ANSI編碼的支持狀況,末尾分析了一個亂碼的案例,出於某知名軟件,你必定不想錯過。windows

Windows的默認編碼?

        偶爾在知乎看到這樣的問題:爲何中文Windows選擇GBK做爲默認編碼?其實會有這樣的誤解也難怪,爲何這麼說呢?你們都從控制檯的Helloworld開始,後來想要輸出中文時天然先想到printf("你好,世界");運行發現真的出現了中文,彷彿英文和中文沒什麼區別,世界很美好的感受。但學習更多以後發現Windows下的strlen("你好,世界")的值居然是10(嚴格說是MinGW下使用GBK做源編碼時或者使用VS時纔是10),第一次感受到了英文字母與漢字的區別,因而咱們去尋找緣由,終於得知GBK編碼之類的各類編碼,也知道了代碼頁這個使人疑惑的名詞。你好世界,世界倒是灰色的。學習

        實際上微軟早就聲明:「UTF-16Little-endian是Microsoft以及Windows操做系統中的編碼標準」。在Windows2000之前的操做系統上,內碼的編碼是和語言相關的(ANSI編碼)。那時候簡體中文版的Windows使用GBK,全部的中文的軟件中的字符串也都是GBK,因此在window上運行也不會亂碼。可是可想而至,這兩者一旦不匹配就會出現亂碼。不一樣語言國家的Windows編碼都不同,所以微軟使用了代碼頁來解釋字符編碼,好比簡體中文版的Windows默認代碼頁就是GBK,這意味着默認使用GBK來解釋字符串,因此能顯示中文是必然的,顯示其餘的語言(好比日語)是亂碼也是必然的。字體

        Windows2000以後(嚴格說是Windows NT 3.1以後)默認使用UTF-16做爲編碼標準。這是什麼意思呢,意思就是全世界的字符你均可以處理,若是安裝了相應的字體,你還能夠顯示所有字符,若是安裝了相關輸入法你還能夠輸入任意一種語言。可是哪些不是UTF-16編碼的程序還能在新平臺下運行嗎?能夠的,在這一方面微軟仍是負責任的,畢竟當初是本身提出的代碼頁方案,不能把軟件開發商們都得罪了。因此直到今天(Windows 10)微軟都是兼容兩者,可是提倡使用UTF-16。那麼Windows的默認編碼是什麼呢?事實是最好不要使用」默認編碼「這個詞,由於根本沒有什麼默認的編碼(你能夠決定使用任何一種編碼,只不過別人不認識而已),推薦使用官方的說法「編碼標準」,並且微軟的編碼標準是UTF-16L。編碼

  之因此不少初學者有誤解,是由於一開始的程序基本都是控制檯程序,而控制檯的默認代碼頁確實是GBK。使用chcp命令能夠查看當前代碼頁,能夠看到回顯Active code page: 936,這正是表明GBK。可使用命令「chcp 65001」切換到UTF-8。控制檯爲了兼容性默認代碼頁是936,不表明Windows的編碼標準是GBK,下面的試驗都在對話框上顯示,由於這是最簡單的檢驗GUI編碼方式的方法。spa

Windows對兩種機制的兼容

那麼具體Windows是怎樣同時兼容兩者:既支持UTF-16,又可使用ANSI編碼的呢?使用一個MessageBox作一下試驗。操作系統

1 #include<windows.h>
2 int main() {
3     MessageBox(NULL, L"你好,世界", L"你好,世界", 0);
4     return 0;
5 }

效果是下面圖1這樣翻譯

     

           圖1                                             圖2代理

         圖3日誌

        但你們都知道,這裏是使用了(如圖2),調用MessageBox()其實是調用了MessageBoxW()MessageBoxW()的參數是wchar_t類型的,wchar_t*的字符串字面量通常被實現成UTF-16編碼。與之對應的是MessageBoxA()MessageBoxA()接受的參數是char*類型的,char*的字符串字面量被實現成ANSI編碼。我說的字符串字面量被實現成某某編碼是什麼意思呢?用圖片解釋一下(圖3),兩個字符串雖然都是「你好,世界」可是運行時的樣子徹底不一樣。要強調機器只認識二進制,因此對機器來講這倆個字符串沒有任何相同點。咱們若是就是調用MassageBoxA(),傳入char*會顯示什麼呢。code

#include<windows.h>
int main() {
    MessageBoxA(NULL, "你好,世界", "你好,世界", 0);
    return 0;
}

       很意外,結果居然和圖1徹底同樣。兩個徹底不一樣的二進制串居然顯示了相同的正確結果。其實這就是所謂的Windows兼容兩種編碼(UTF-16與ANSI編碼),雖然推薦使用UTF-16可是,使用GBK也能正常在Windows的GUI中顯示,可是你的應用程序已經被Windows歸類爲「非Unicode程序」。這裏你們能夠打開控制面板--時鐘語言和區域--區域--管理,能夠找到一個設置項:非Unicode程序的語言。能夠選擇中文或者其餘語言,那它有什麼影響呢?你不妨嘗試一下改爲日語或者韓語什麼的,再運行第二段代碼,你就能看到亂碼了。可是除此以外幾乎感覺不到影響,程序們仍是正常的運行着,沒有出現亂碼,這說明如今的絕大多數程序都是使用的UTF-16編碼,因此這一個設置對他們根本沒有影響。

        但也並不絕對,在筆者把這一項設置修改成「日語(日本)」一週後(我已經忘了本身沒有改回來,由於確實沒有什麼影響)看到一個奇怪的文件夾,他的名字是:ムクタラマツヤリ。是哪個程序搞出了這樣的亂碼呢?文件夾名字原來是什麼呢(不要期望這是日語,這只是日語字符組成的亂碼,不能看出含義)?下一節咱們來分析這個例子,揭開這個還在使用ANSI編碼的程序的羞恥的面紗。

一個亂碼的例子分析

        日本的ANSI編碼是Shift_JIS,它是在有Unicode以前日本國內計算機的編碼方式。可想而知,之因此出現ムクタラマツヤリ這一段亂碼,就是由於我把非Unicode程序的語言設定成了日語(日本),因此致使某個想要用GBK字符串命名文件夾的程序建立了亂碼的名稱。如今可以查到這些字符的Unicode編碼(複製粘貼後,他已經變成了Unicode),因此把Unicode轉換成的Shift_JIS二進制串解釋爲GBK就獲得文件夾原本的名字了。

ff fe 91 ff 78 ff 80 ff 97 ff 8f ff 82 ff 94 ff 98 ff<--這是UTF16小端編碼,開頭的0xfffe是BOM,不知道BOM是什麼的能夠查看詳解字符編碼[一]

d1 b8 c0 d7 cf c2 d4 d8<--這是對應的Shift_JIS

把上面的二進制翻譯成GBK就是答案:迅雷下載

我用的正是最新版本的迅雷:9.1.41.914。迅雷的一個小Bug就這樣被我發現了。

日誌:在把非Unicode程序的語言改成日語後,只有一個MFC的上古程序和最新版的迅雷出現了亂碼,2017年10月9日。

 下一篇會介紹C/C++程序避免亂碼的方法並介紹怎樣在Java中處理UTF-16的代理對。喜歡請給個推薦,再見。

相關文章
相關標籤/搜索