石建 | Fat Mind

          編碼的一些理解


          已遷往  
          http://fatmind.iteye.com


          主要參考:

          1.寶寶的文章《中文化和國際化問題淺析》

             2.阮一峰的網絡日志

                http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html

                http://www.ruanyifeng.com/blog/2010/02/url_encoding.html

          一、編碼基礎知識

          1. 為什么要編碼、解碼

          計算機存儲的最小單位是“位bit”,存儲的所有內容都是一串二進制的表示。但最小的存儲單元是byte,內存的編址也是以字節為單位,大部分的計算機系統都是以字節為單位進行存儲、計算、傳輸的。導致:字符(多字節表示)與字節的相互轉換。

          概念:編碼:字符轉換為字節;解碼:字節轉化為字符。

          2.    常見基本字符集

          字符集的概念:在確定的規則下,一個value與一個字符的對應關系。

          ASCII
           :只使用了一個8位字節中的低7位,總共是128個編碼位。(最早由美國制定,定義英文字符與二進制位之間的關系)

             Iso8859-1單字節編碼,8位全部使用,涵蓋了大多數的西歐文字。(Unicode的最開頭256個字符編碼和ISO-8859-1是一一對應)

             gb2312gbkgb18030 : gb23122字節)是依據iso的標準制定、gbk2字節)是國家技術監督局1995年為中文Windows 95所制定的新的漢字內碼規范、gb18030全稱是《信息交換用漢字編碼字符集》,是我國的強制標準(包含日韓等,支持單字節、雙字節、4字節編碼,中國大陸的標準)

          ISO制定了ISO 2022標準,提供了七位與八位編碼字符集的擴充方法的標準,gb2312是根據ISO 2022標準制定。

          多字節編碼,只是對中文及相關字符進行編碼,可以認為是ascii的擴充(問題:如何區分ascii碼與gb?)。gb2312規定:2字節編碼,只使用每個字節的低7位,高位為0。結果:在計算機內部無法區分ascii碼與gb碼,出現“機內碼(計算機內部表示漢字的編碼),gb每個字節最高為置為1。字符集依次增長的過程,向下兼容。

              為什么存在區位碼?不理解,請大家指點。

           

              Unicode UTFUCS Transformation Format的縮寫(深入了解:參考UCS的結構)。Unicode只是一個符號集,它只規定了符號的二進制代碼,每一個符號都給予一個獨一無二的編碼,卻沒有規定這個二進制代碼應該如何存儲。

                  問題:漢字unicode是十六進制數4E25,兩個字節。如何才能區別unicodeascii,計算機怎么知道2個字節表示一個符號,而不是分別表示2個符號呢?unicode沒有規定具體的編碼方式。

          常用的unicode實現方式有UTF-8UTF-16容納全世界的編碼,優點:中文的網頁,在英文版的瀏覽器也能正常顯示,不會亂碼。

           

          UTF-16:定長雙字節

          缺點:數據量增大,如果對于英文字符,增加2倍;屬于ascii字符,高字節填補為0x00,在C里面是字符串的結尾,帶來無數問題

          優點:直接表現字符編碼的整數值,所以UTF-16是最直接的Unicode表示法。它是定長的,這大大簡化了字符串的操作,Java語言就是用UTF-16格式將字符存儲在內存中的。

                  

              UTF-8:變長字節編碼(1-6字節)

                       1)對于單字節的符號,字節的第一位設為0,后面7位為這個符號的unicode碼。
                       2)對于n字節的符號(n>1),第一個字節的前n位都設為1,第n+1位設為0,后面字節的前兩位一律設為10。剩余的二進制位,全部為這個符號的unicode碼。

                簡單結論:

          1.       如果一個字節,最高位(第8位)為0,表示這是一個ASCII字符(00 - 7F)。可見,所有ASCII編碼已經是UTF-8了。

          2.       如果一個字節,以11開頭,連續的1的個數暗示這個字符的字節數,例如:110xxxxx代表它是雙字節UTF-8字符的首字節。

          3.       如果一個字節,以10開始,表示它不是首字節,需要向前查找才能得到當前字符的首字節。

          4.       ascii單字節編碼,中文屬于3字節范圍。

           

          3. 為什么亂碼

          亂碼有很多種情形,但是萬變不離其宗,就是“解碼所用的charset和編碼所用的charset不兼容”。

              Unicode規定,編碼時,碰到看不懂的字符,一律用“?0x3F表示。

          Unicode規定,解碼時,發現看不懂的字節,一律用?0xFFFD表示。

           

          4. 引用“寶寶總結”

          1.             字符編碼是抽象字符在計算機中的數字表示。

          2.             字符編碼集(character set,簡稱字符集)是一批字符編碼的集合。世界上存在大量互不兼容的字符集,給國際交流帶來了困難。

          3.             ASCII碼是最古老的字符編碼,它總共只定義了7位共128個字母、數字和符號。但它是其它所有字符編碼的基礎。

          4.             Unicode常用UTF-8UTF-16來表示。7位的ASCII碼不用作任何變化,就已經是UTF-8了。但UTF-8需要用3個字節來表示一個漢字。

          5.             ISO 8859系列字符集,定義了單字節字符編碼的標準。其中最特殊的是ISO-8859-1編碼,它的編碼和Unicode中最開始的256個字符編碼完全相同。

          6.             GB18030編碼是中國大陸的國家標準,在字匯上等同于Unicode,在編碼上和GB2312編碼以及GBK編碼兼容。

           

          二、Java編碼問題

           

          寶寶:分析Java中文亂碼問題的根本原因,首先要了解這些中文字符的輸入源,其次是了解這些字符被輸出到用戶瀏覽器經過了哪些轉換和輸出環節

          中文字符可以來源于:

          1.       程序內嵌的中文,源代碼里直接寫中文字符串。

          2.       外部讀入的中文。

          1. 程序內嵌中文:

          因為Java源代碼(.java)本身是一個文本文件,所以和讀普通文本文件一樣,編譯器(javac)必須以字節流的方式讀入文件內容,并以適當的編碼轉換為Unicode字符存儲在Java字節碼文件(.class)中。例如 Java源代碼文件中包含GBK編碼的中文字符,則使用下面的命令編譯:

          javac -encoding GBK MyClass.java

          如果不指定-encoding參數,javac會使用JVM默認的編碼(Java虛擬機取操作系統編碼 在中文Windows上,默認是GB18030,在英文Linux上,默認是ISO-8859-1。因此,如果文件是在英文Linux下編譯而未指定-encoding,那么文件中的中文我愛Alibaba”就會變成“ÎÒ°®Alibaba”

          Jsp <%@page contentType="text/html; charset=GBK"%>  //決定讀入文件編碼和輸出數據流編碼

          2. 外部讀入的中文

                比如:數據庫讀入、文件讀入、用戶數據的提交

           

          用戶數據的提交url串、post

              一般來說,URL只能使用英文字母、阿拉伯數字和某些標點符號。這是因為網絡標準RFC 1738做了硬性規定:

              只有字母和數字[0-9a-zA-Z]、一些特殊符號“$-_.+!*'(),”[不包括雙引號]、以及某些保留字,才可以不經過編碼直接用于URL

              這意味著,如果URL中有漢字,就必須編碼后使用。但是麻煩的是,RFC 1738沒有規定具體的編碼方法,而是交給應用程序(瀏覽器)自己決定。這導致“URL編碼成為了一個混亂的領域。

          一些結論

          1. 網址路徑的編碼,用的是utf-8編碼. (firefor&IE相同)

          http://www.taobao.com/春節 %E6%98%A5%E8%8A%82

          2. 直接在瀏覽器輸入查詢字符的編碼,不同的瀏覽器有不同的處理.

          http://www.taobao.com/?q=春節

          chrome : %E6%98%A5%E8%8A%82

          firefor(中文版):  %B4%BA%BD%DA

               3. GETPOST方法提交的編碼,由網頁的編碼決定.

                Baidu / google 搜索“春節”,url串的結果

               4. Ajax調用的URL包含漢字, IE總是采用GB2312編碼(操作系統的默認編碼),而Firefox總是采用utf-8編碼. (未驗證)

           

          3. 數據在內存中的編碼

          Javaunicode(實現unicode-16)編碼,Java能夠實現unicode與其它字符集之間的轉換。比如: GBK的字節串d6 d0轉換成utf8字節串e4 b8 ad的過程是:GBK bytes (解碼) -> unicode (編碼) -> UTF8 bytes(解碼),而不是gbk -> utf-8


          4. 數據輸出

            4.1 Servlet輸出
            
          字符流方式 response.getWriter()。用來輸出文本類型的內容,如HTML和純文本。 在調用response.getWriter()前,我們必須設置content type response.setContentType("text/html; charset=GBK")

            response.getWriter()通過content type中指定的字符編碼來決定如何將字符流轉換成字節流。

            4.2 瀏覽器如何確定頁面的字符編碼

              瀏覽器收到從WEB服務器返回的頁面時

          1.首先檢查HTTP響應中指定的contentType,也就是servlet通過response.setContentType方法設置的值。如果content type中指定字符編碼(例如text/html; charset=GBK),則使用這種方式解碼這個頁面。

                2.如果HTTP響應中沒有指定字符集,那么瀏覽器會檢查HTML頁面中是否包含:

          <meta http-equiv="Content-Type" content="text/html; charset=GBK">如果找到,則使用這里指定的字符編碼

          3.如果既沒有在HTTP響應中指定字符編碼,也沒有在HTML內容中指定字符編碼,則瀏覽器根據一定的規則自動確定頁面的字符編碼(字符集探測)。例如,在英文環境中,瀏覽器會使用ISO-8859-1,簡體中文環境中,則使用GBK

           

          三、總結:

          分析Java中文亂碼問題的根本原因,首先要了解這些中文字符的輸入源,其次是了解這些字符被輸出到用戶瀏覽器經過了哪些轉換和輸出環節。

                    亂碼有很多種情形,但是萬變不離其宗,就是:解碼所用的charset和編碼所用的charset不兼容

           

           

          posted on 2010-10-05 21:12 石建 | Fat Mind 閱讀(388) 評論(0)  編輯  收藏 所屬分類: 一點理解

          導航

          <2010年10月>
          262728293012
          3456789
          10111213141516
          17181920212223
          24252627282930
          31123456

          統計

          常用鏈接

          留言簿

          隨筆分類

          隨筆檔案

          搜索

          最新評論

          What 、How、Why,從細節中尋找不斷的成長點
          主站蜘蛛池模板: 沙雅县| 筠连县| 漠河县| 浠水县| 大同市| 新源县| 五莲县| 德惠市| 仙桃市| 彰化市| 克什克腾旗| 缙云县| 定南县| 广汉市| 凤庆县| 麟游县| 同仁县| 祁连县| 应用必备| 大名县| 平泉县| 会宁县| 台北县| 犍为县| 昌乐县| 莫力| 浪卡子县| 延边| 玉山县| 房产| 济源市| 高邑县| 即墨市| 西安市| 长子县| 仪征市| 浦城县| 安阳县| 灵山县| 万山特区| 天镇县|