posts - 19, comments - 1, trackbacks - 0, articles - 0
            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

          字符編碼應用的一個奇怪現象

          Posted on 2006-09-28 11:36 xyang 閱讀(298) 評論(0)  編輯  收藏
          [概述]
          ????? 在Windows操作系統中使用記事本新建一個文本文件,在文件里面寫入“聯通”兩個字并保存。當再次打開這個文本文件時候,在記事本中看到得卻不是剛剛輸入的“聯通”,而是亂碼。網絡上有人把這個奇怪現象包裝成把戲,如果你曾遇到過這種把戲就會知道,他們往往讓你建立兩個文本文件進行對比,其中一個輸入“聯通”,另外一個可能是“移動”等等,最后試圖八卦地讓你相信聯通、移動和微軟之間有著種種恩怨情仇。

          [解釋]
          ????? 這是一個字符編碼應用的奇怪現象,講的明白點,可以說是記事本開小差了!記事本為什么會犯錯誤?記事本犯了怎樣的錯誤呢?也許你會迫不及待的想知道這些問題,如果是這樣,我不會讓你空腹而歸的。
          ????? 在簡體中文操作系統中默認的本地字符集編碼是GBK編碼,除非你在保存記事本文本文件時候選擇了其他編碼方式,否則用記事本錄入的字符信息將使用GBK編碼進行儲存。巧合的是,“聯通”這兩個字符的GBK編碼具有UTF-8編碼的特征,記事本犯下的錯誤正是將GBK編碼存放的記錄有“聯通”兩個字符的文件誤認為UTF-8編碼的文件。或許你會問,UTF-8編碼的文件不是以“EF BB BF”三個特殊字節開頭嗎?既然這樣,記事本怎么會犯這么低級的錯誤呢?沒錯,UTF-8編碼規定使用UTF-8編碼的文件以“EF BB BF”三個特殊字節開頭,但并不是強制性要求,早期的UTF-8編碼文件就不遵循這個規定。因此記事本不能依靠文件的開頭字節判斷一個文件是否是UTF-8編碼,而只能對文件中的數據進行簡單的編碼分析來確定。正是這個原因,才有了字符編碼應用中的這個奇怪又無法避免的現象。

          [細節]
          ????? 如果上面的解釋對于你來說只是杯開胃紅酒,那我還是塊點把主食呈上吧,一份大峽谷香烤豬肋排。UTF-8編碼采用1-3個字節對字符進行編碼,編碼字節數與字符的Unicode編碼值有嚴格的對應關系,讓我們回憶下UTF-8編碼和Unicode的對應關系吧。

          ????? Unicode編碼值???????????????????????????? ?UTF-8編碼結構
          ????? \u0001 - \u007E?????????????????????????? 0XXXXXXX
          ????? \u0080 - \u07FF 和 \u0000?????????? ?110XXXXX 10XXXXXX
          ????? \u0800 - \uFFFF??????????????????????????? 1110XXXX 10XXXXXX 10XXXXXX

          ????? “聯通”這兩個字符的GBK編碼值是“C1 AA CD A8",GBK編碼方式使用兩個字節對一個字符進行編碼,因此以GBK編碼方式存放的錄有“聯通”兩個字符的文件的大小為四個字節。接下來分別觀察“聯通”這兩個字符GBK編碼值的二進制形式,你有發現有趣的事。

          ????? 聯??? GBK??? 十六進制:C1 AA??? 二進制:1100 0001,1010 1010
          ????? 通??? GBK??? 十六進制:C1 AA??? 二進制:1100 1101,1010 1000

          ????? 請注意上面二進制數據的著色部分,你想到了什么?對,它們和UTF-8編碼結構中的補充位完全一致,UTF-8編碼的補充位使得編碼值更有規律,而記事本剛好憑借這個特征區分UTF-8編碼的文件。存有“聯通”兩個字符的文件的所有數據都符合這個特征,就是這樣,記事本徹底的將文件誤認為UTF-8編碼的文件。
          ????? 將錯就錯,讓我們來看看這個錯誤是怎樣收場的。如果把“聯通”的GBK編碼值當作UTF-8編碼值,那文件就成為一個寫有數據“C1 AA CD A8”并以UTF-8編碼的文件,當使用記事本再次打開的時候會看到什么呢?只要將UTF-8編碼轉換成Unicode編碼就知道了。UTF-8編碼“C1 AA CD A8”轉換成Unicode編碼后,編碼值為“6A 00 68 03”(轉換方法請參考本Blog中的《字符編碼》一文)。0x006A這個Unicode編碼值位于\u0001 - \u007E之間,若要轉換為UTF-8編碼,顯然只能用一個字節進行編碼,因此“聯”的GBK編碼“C1 AA”雖然特征上貌似UTF-8編碼,但它卻不對應任何一個UTF-8編碼。接著看0x0368這個Unicode編碼值,這個值對應了字符“?”,這也正是我們將在記事本中看到的內容。或許你會說我看到的是一個黑色矩形啊,這只是字體的原因,你將字體改為宋體或者其他字體,看到的就是字符“?”。
          ????? 對于中文字符,UTF-8編碼要用三個字節進行編碼,因此,如果你使用記事本錄入“聯通”,然后選擇以UTF-8編碼方式保存的話,文件大小應為9個字節(包含三個字節的開頭數據),而同樣的文件GBK編碼卻是4個字節。最后附上“聯通”的GBK、UTF-8、Unicode編碼值,以及記事本的錯誤思維。

          ????? 聯通??GBK? C1 AA CD A8??? UTF-8? E8 81 94 E9 80 9A??? Unicode? 54 80 1A 90
          ????? 聯通? GBK? C1 AA CD A8??? UTF-8? C1 AA CD A8????????????Unicode? 6A 00 68 03? (將GBK值誤認為UTF-8值的結果)

          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 濮阳市| 溧水县| 深泽县| 衡山县| 肇源县| 全南县| 西盟| 麦盖提县| 延长县| 会泽县| 临海市| 沙洋县| 都安| 金华市| 克拉玛依市| 革吉县| 芮城县| 南宁市| 新巴尔虎左旗| 铜鼓县| 德昌县| 惠水县| 咸丰县| 徐水县| 桐庐县| 慈利县| 永登县| 隆昌县| 宣威市| 红安县| 益阳市| 新干县| 临清市| 连州市| 新乡县| 苏尼特右旗| 汾阳市| 工布江达县| 大邑县| 扎兰屯市| 普兰县|