<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    優秀是一種習慣

     

    Java基礎——孫鑫談Java中文亂碼問題產生原因分析(一)

    聲明:本文轉載自:http://www.phome.asia/article/814.html

    在計算機中,只有二進制的數據,不管數據是在內存中,還是在外部存儲設備上。對于我們所看到的字符,也是以二進制數據的形式存在的。不同字符對應二進制數的規則,就是字符的編碼。字符編碼的集合稱為字符集。

    常用字符集

    1. ASCII

    在早期的計算機系統中,使用的字符非常少,這些字符包括26個英文字母、數字符號和一些常用符號(包括控制符號),對這些字符進行編碼,用1個字節就足夠了(1個字節可以表示28=256種字符)。然而實際上,表示這些字符,只使用了1個字節的7位,這就是ASCII編碼1ASCII

    ASCIIAmerican Standard Code for Information Interchange,美國信息互換標準代碼),是基于常用的英文字符的一套電腦編碼系統。每一個ASCII與一個8位(bit)二進制數對應。其最高位是0,相應的十進制數是0127。例如,數字字符“0”的編碼用十進制數表示就是48。另有128個擴展的ASCII碼,最高位都是1,由一些圖形和畫線符號組成。ASCII是現今最通用的單字節編碼系統。

    ASCII用一個字節來表示字符,最多能夠表示256種字符。隨著計算機的普及,許多國家都將本地的語言符號引入到計算機中,擴展了計算機中字符的范圍,于是就出現了各種不同的字符集。

    2ISO8859-1

    因為ASCII碼中缺少£、ü和許多書寫其他語言所需的字符,為此,可以通過指定128以后的字符來擴展ASCII碼。國際標準組織(ISO)定義了幾個不同的字符集,它們是在ASCII碼基礎上增加了其他語言和地區需要的字符。其中最常用的是ISO8859-1,通常叫做Latin-1Latin-1包括了書寫所有西方歐洲語言不可缺少的附加字符,其中0127的字符與ASCII碼相同。ISO 8859另外定義了14個適用于不同文字的字符集(8859-28859-15)。這些字符集共享0127ASCII碼,只是每個字符集都包含了128255的其他字符。

    3GB2312GBK

    GB2312是中華人民共和國國家標準漢字信息交換用編碼,全稱《信息交換用漢字編碼字符集-基本集》,標準號為GB2312-80,是一個由中華人民共和國國家標準總局發布的關于簡化漢字的編碼,通行于中國大陸和新加坡,簡稱國標碼。

    因為中文字符數量較多,所以采用兩個字節來表示一個字符,分別稱為高位和低位。為了和ASCII碼有所區別,中文字符的每一個字節的最高位都用1來表示。GB2312字符集是幾乎所有的中文系統和國際化的軟件都支持的中文字符集,也是最基本的中文字符集。它包含了大部分常用的一、二級漢字和9區的符號,其編碼范圍是高位0xa1-0xfe,低位也是0xa1-0xfe,漢字從0xb0a1開始,結束于0xf 7fe

    為了對更多的字符和符號進行編碼,由前電子部科技質量司和國家技術監督局標準化司于199512月頒布了GBKK擴展的漢語拼音第一個字母)編碼規范,在新的編碼系統里,除了完全兼容GB2312外,還對繁體中文、一些不常用的漢字和許多符號進行了編碼。它也是現階段Windows和其他一些中文操作系統的默認字符集,但并不是所有的國際化軟件都支持該字符集。不過要注意的是GBK不是國家標準,它只是規范。GBK字符集包含了20 902個漢字,其編碼范圍是0x8140-0xfefe

    每個國家(或區域)都規定了計算機信息交換用的字符編碼集,這就造成了交流上的困難。想像一下,你發送一封中文郵件給一位遠在西班牙的朋友,當郵件通過網絡發送出去的時候,你所書寫的中文字符會按照本地的字符集GBK轉 換為二進制編碼數據,然后發送出去。當你的朋友接收到郵件(二進制數據)后,查看信件時,會按照他所用系統的字符集,將二進制編碼數據解碼為字符,然而由 于兩種字符集之間編碼的規則不同,導致轉換出現亂碼。這是因為,在不同的字符集之間,同樣的數字可能對應了不同的符號,也可能在另一種字符集中,該數字沒 有對應符號。

    為了解決上述問題,統一全世界的字符編碼,由Unicode協會制定并發布了Unicode編碼。

    4Unicode

    Unicode(統一的字符編碼標準集)使用065 535的雙字節無符號數對每一個字符進行編碼。它不僅包含來自英語和其他西歐國家字母表中的常見字母和符號,也包含來自古斯拉夫語、希臘語、希伯來語、阿拉伯語和梵語的字母表。另外還包含漢語和日語的象形漢字和韓國的Hangul音節表。

    目前已經定義了40 000多個不同的Unicode字符,剩余25 000個空缺留給將來擴展使用。其中大約20 000個字符用于漢字,另外11 000左右的字符用于韓語音節。Unicode0255的字符與ISO8859-1中的一致。

    Unicode編碼對于英文字符采取前面加“0”字節的策略實現等長兼容。如“a”ASCII碼為0x61Unicode碼就為0x000x61

    5UTF-8

    使用Unicode編碼,一個英文字符要占用兩個字節,在Internet上,大多數的信息都是用英文來表示的,如果都采用Unicode編碼,將會使數據量增加一倍。為了減少存儲和傳輸英文字符數據的數據量,可以使用UTF-8編碼。

    UTF-8全稱是Eight-bit UCS Transformation FormatUCSUniversal Character Set,通用字符集,UCS是所有其他字符集標準的一個超集)。對于常用的字符,即0127ASCII字符,UTF-8用一個字節來表示,這意味著只包含7ASCII字符的字符數據在ASCIIUTF-8兩種編碼方式下是一樣的。如果字符對應的Unicode碼是0x0000,或在0x00800x007f之間,對應的UTF-8編碼是兩個字節,如果字符對應的Unicode碼在0x08000xffff之間,對應的UTF-8編碼是三個字節。因為中文字符的Unicode編碼在0x08000xffff之間,所以數據如果是中文,采用UTF-8編碼數據量會增加50%

    UnicodeUTF-8轉換的規則簡述如下:

    1)如果Unicode編碼的16位二進制數的前9位是0,則UTF-8編碼用1個字節來表示,這個字節的首位是“0”,剩下的7位與原二進制數據的后7位相同。例如:

    Unicode編碼:"u0061 = 00000000 01100001

    UTF-8編碼:01100001 = 0x61

    2)如果Unicode編碼的16位二進制數的頭5位是0,則UTF-8編碼用2個字節來表示,首字節以“110”開頭,后面的5位與原二進制數據除去前5個零后的最高5位相同;第二個字節以“10”開頭,后面的6位與原二進制數據中的低6位相同。例如:

    Unicode編碼:"u00A9 = 00000000 10101001

    UTF-8編碼:11000010 10101001 = 0xC2 0xA9

    3)如果不符合上述兩個規則,則用三個字節表示。第一個字節以“1110”開頭,后四位為原二進制數據的高四位;第二個字節以“10”開頭,后六位為原二進制數據中間的六位;第三個字節以“10”開頭,后六位為原二進制數據的低六位。例如:

    Unicode編碼:"u4E2D = 01001110 00101101

    UTF-8編碼:11100100 10111000 10101101 = 0xE4 0xB8 0xAD

    UTF-8編碼的多字節串中,第一個字節開頭“1”的數目就是整個字符串中字節的數目。

    17.1.2 對亂碼產生過程的分析

    為了讓使用Java語言編寫的程序能在各種語言的平臺下運行,Java在其內部使用Unicode字符集來表示字符,這樣就存在Unicode字符集和本地字符集進行轉換的過程。當在Java中讀取字符數據的時候,需要將本地字符集編碼的數據轉換為Unicode編碼,而在輸出字符數據的時候,則需要將Unicode編碼轉換為本地字符集編碼。

    例如,在中文系統下,從控制臺讀取一個字符,實際上讀取的是GBK編碼0xD6D0,在Java語言中要將GBK編碼轉換為Unicode編碼0x4E2D,此時,在內存中,字符對應的數值就是0x4E2D,當我們向控制臺輸出字符時,Java語言將Unicode編碼再轉換為GBK編碼,輸出到控制臺,中文系統再根據GBK字符集畫出相應的字符。

    從上述過程來看,讀取和寫入的過程是可逆的,那么理應不會出現中文亂碼問題。然而,實際應用的情形,比上述過程要復雜得多。在Web應用中,通常都包括了瀏覽器、Web服務器、Web應用程序和數據庫等部分,每一部分都有可能使用不同的字符集,從而導致字符數據在各種不同的字符集之間轉換時,出現亂碼的問題。

    Java語言中,不同字符集編碼的轉換,都是通過Unicode編碼作為中介來完成的。例如,GBK編碼的字符要轉換為ISO-8859-1(同ISO8859-1)編碼,其過程如下:

    1)因為在Java中的字符,都是用Unicode來表示的,所以GBK編碼的字符要轉換為Unicode表示:0xD6D0->0x4E2D

    2)將字符Unicode編碼轉換為ISO-8859-1編碼,因為Unicode編碼0x4E2DISO-8859-1中沒有對應的編碼,于是得到0x3f,也就是字符“?”

    下面的代碼演示了這一過程:

    //GBK編碼的字符轉換為Unicode編碼表示

    String str="";

    //將字符Unicode編碼轉換為ISO-8859-1編碼

    byte[] b=str.getBytes("ISO-8859-1");

    for(int i=0;i<b.length;i++)

    {

           //輸出轉換后的二進制代碼。

           System.out.print(b[i]);

    }

    當從Unicode編碼向某個字符集轉換時,如果在該字符集中沒有對應的編碼,則得到0x3f(即問號字符?)。這就是為什么有時候我們輸入的是中文,在輸出時卻變成了問號。

    從其他字符集向Unicode編碼轉換時,如果這個二進制數在該字符集中沒有標識任何的字符,則得到的結果是0xfffd。例如一個GBK的編碼值0x8140,從GB2312Unicode轉換,然而由于0x8140不在GB2312字符集的編碼范圍(0xa1a1-0xfefe),當然也就沒有對應任何的字符,所以轉換后會得到0xfffd。下面的代碼演示了這一過程。

    //構造一個二進制數據。

    byte[] buf={(byte)0x81,(byte)0x40,(byte)0xb0,(byte)0xa1};

    //將二進制數據按照GB2312Unicode編碼轉換。

    String str=new String(buf,"GB2312");

           

    for(int i=0;i<str.length();i++)

    {

        //取出字符串中的每個Unicode編碼的字符。

        char ch=str.charAt(i);

        //將該字符對應的Unicode編碼以十六進制的形式輸出。

        System.out.print(Integer.toHexString((int)ch));

        System.out.print("--");

        //輸出該字符。

        System.out.println(ch);

    }

    posted on 2009-08-12 09:41 黃土高坡 閱讀(446) 評論(0)  編輯  收藏


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


    網站導航:
     

    導航

    統計

    常用鏈接

    留言簿

    隨筆分類

    隨筆檔案

    收藏夾

    搜索

    最新評論

    閱讀排行榜

    評論排行榜

    主站蜘蛛池模板: 亚洲一级毛片免费看| 亚洲色图在线播放| 亚洲aⅴ天堂av天堂无码麻豆| 99在线免费观看视频| 久久亚洲AV无码精品色午夜麻| 99久久免费国产精品热| 亚洲色无码一区二区三区| 国产精品免费久久久久久久久| 在线观看亚洲成人| 99视频在线免费观看| 亚洲老妈激情一区二区三区| 国产一级淫片a免费播放口| 亚洲一区二区三区电影| 久久国产高潮流白浆免费观看 | 日本三级2019在线观看免费| 亚洲喷奶水中文字幕电影 | xxxx日本在线播放免费不卡| 亚洲人成在线播放网站| 免费观看成人久久网免费观看| 久久精品九九亚洲精品| 成人免费无码视频在线网站| 亚洲爆乳少妇无码激情| 亚洲一级特黄大片在线观看| 最近免费中文字幕MV在线视频3 | 亚洲av永久综合在线观看尤物| 午夜网站免费版在线观看| 成年网在线观看免费观看网址| 亚洲一区二区女搞男| 97人妻无码一区二区精品免费| 99亚洲精品卡2卡三卡4卡2卡| 亚洲午夜福利在线观看| 国产免费毛不卡片| 一级黄色片免费观看| 无码乱人伦一区二区亚洲| 中文字幕无码成人免费视频| 成人福利在线观看免费视频| 亚洲av片劲爆在线观看| 日韩免费视频网站| 久久免费区一区二区三波多野| 亚洲天堂男人影院| 亚洲精品亚洲人成在线观看|