網上流傳著一個笑話,說微軟和聯通有仇,內容大致如下:
如果你的電腦作業系統是WIN2000或WINXP的話,那麼:
1. 在案頭上點右鍵,選擇建立 — 文字文件;
2. 開啟"建立 文字文件",錄入"移動"兩字後儲存後關掉
3. 重新開啟"建立 文字文件",看到什麼了?是不是剛剛錄入的"移動"兩字?
4. 把"移動"分別換成"電信"和"網通",重複1--3步,是不是也都沒什麼問題?
5. 現在我們拿"聯通"來試試,重複1--3步,你會發現剛剛錄入的"聯通"兩字不見了,取而代之是個燒焦的手機電池(一個符號)。 看來微軟確實跟聯通有仇呀!
笑話當然是笑話,不能當真。但為什麼會這樣呢?是微軟的bug嗎?確實有點像,不過——微軟是世界頂級的軟體公司,記事本則有可能是windows中最簡單應用程式,說這是bug未免有點不合情理吧?
好了,既然把自己的主觀臆斷否定了,就讓我們踏上尋找事實真相的艱苦曆程吧:)。
不知你注意過沒有,記事本的開啟、儲存對話方塊比普通的檔案對話方塊多一個編碼選項,可以通過它指定檔案的編碼是UNICODE、ANSI還是UTF8。"喔,我知道了",你可能會說,"這肯定是windows api IsTextUnicode惹的禍。因為文字檔本身不儲存編碼資訊,所以記事本開啟檔案時就要調用IsTextUnicode來判斷檔案的編碼。而IsTextUnicode是根據文本的內容猜測其編碼,所以肯定是它猜錯編碼格式了。想想‘聯通'只有兩個字,這樣的錯誤有情可原,OK了,問題解決了"。
說實話,一開始我也是這麼想的,但後來發現,我犯了兩個錯誤。①IsTextUnicode並沒有猜錯,不信你可以檢查一下IsTextUnicode("聯通", 4, NULL)的傳回值。②記事本有可能儲存編碼資訊,這個後面再說。
原來,記事本除了判斷編碼是不是UNICODE以外,還要判斷它是不是UTF8。"聯通"兩個字的代碼是(位元組順序從低到高):C1 AA CD A8,轉換為二進位是:11000001 10101010 11001101 10101000。對照UTF8編碼方案(詳情請見http://www.cis.ohio-state.edu/htbin/rfc/rfc2279.html):
0000-007F之間的字元不做轉換
0080-07FF之間的編碼為110xxxxx 10xxxxxx
0800-FFFF之間的編碼為1110xxxx 10xxxxxx 10xxxxxx
不難發現,"聯通"的編碼符合第二種情況,所以記事本把它判定為UTF8編碼,而對其進行解碼後,將變成00000000 01101010 00000011 01101000。注意:前兩個位元組解碼後並不在0080--07FF之間,所以被認為是錯誤的值,忽略了。後面兩個位元組經過調整位元組順序後,將變為16進位的0x0368,也就是那塊燒毀的電池了(取決於所使用的字型)。
PS:
1. 如果你儲存檔案時,指定使用除ANSI以外的編碼,記事本將用檔案開頭的幾個位元組儲存檔案編碼,UNICODE對應0xFEFF,UNICODE BIG ENDIAN對應0xFFFE,UTF-8對應0xBFBBEF。這幾個位元組被稱為BOM(我也不知道是那幾個單詞的縮寫)。如果檔案有BOM,記事本直接使用它判斷編碼,否則它就根據檔案內容判斷編碼。
2. 分析的過程中我用ultra edit來查看檔案的16進位內容,但它會自動進行編碼轉換並給檔案加上一個BOM,導致看到的和實際不符(檔案4位元組,到了ultraedit中就成了6位元組),讓我走了一些彎路。