好久沒有來鋤草了,最近遇到編碼問題,記錄一下
主要包括區位碼,GB,GB2312,UNICODE,UTF-8,ISO8859-1。大部分爲轉載,如有錯誤,請高人指正。
GB2312和ISO8859-1無法直接轉換,這個問題頭疼,不過網上還是有解決方案的,如果是這個問題,大家就狗狗一下吧。
以下內容主要來自:http://www.mao2.com/papers/zhongwenbianma.html
和
http://hi.baidu.com/cuifenghui/blog/item/735b55da1804a1dfb7fd4817.html
1.區位碼:
我國國家標準漢字信息交換用編碼,全稱《信息交換用漢字編碼字符集基本集》,標準號爲 GB 2312-80(GB 是“國標”二字的漢語拼音縮寫),由中華人民共和國國家標準總局發佈,1981年5月1日實施,新加坡等地也使用這一編碼。區位碼是一個四位的十進制數,它的前兩位叫做區碼,後兩位叫做位碼。區位碼的編碼範圍是:0101~9494。比如,按拼音排序後的第一個漢字是“啊”,它的區位碼爲1601(十六進制爲1001 H)。下圖是新華字典所顯示的漢字“啊”的信息。
2. 漢字國標碼(GB):
又稱交換碼,是一個碼集。將區位碼的十進制行列值轉換成16進制,再分別加上20H得到。舉例:將“萬”的區位碼轉換爲16進製表示:2D 52,分別加上20H得:4D 72,即爲國標碼。
由此,漢字第一個字節的最小值:10H + 20H =30H, 最大值:57H + 20H = 77H
第二個字節最小值: 01H + 20H = 21H, 最大值: 5EH + 20H = 7EH
3.GB2312 碼:
又稱爲國標碼,在區位碼的基礎上,分別將區號值和位號值各加 32(20H)而得到,於是它的編碼範圍是2121H~7E7EH,於是“啊”的GB2312 的編碼爲3021 H(即1001H+2020H)。但是,因爲GB2312 與ASCII有重疊,通行方法是將GB2312 碼兩個字節的最高位置1以示區別,編碼範圍變爲 0A1A1H~0FEFEH(實際只用到0xF7FEH)。這種在第八位置"1",提示計算機轉入雙字節的編碼方式也叫EUC(Extended UNIX Code)編碼,所以“啊”的GB2312 的最終編碼爲B0A1 H(即3021 H+8080H)。其實GB2312 和區位碼是一樣的,應該合在一起談。但事實上兩個的編碼又不一樣,我也不知爲什麼要弄得如此複雜。或許可以這樣理解:區位碼是一個編碼方案,而 GB2312 編碼則是這個方案在計算機裏的一個具體實現。GB2312 將收錄的漢字分成兩級:第一級是常用漢字計3755個,置於16-55區,按漢語拼音字母/筆形順序排列;第二級漢字是次常用漢字計3008個,置於 56-87區,按部首/筆畫順序排列。共計6763個漢字。
GB2312(1980年)一共收錄了7445個字符,包括6763個漢字和682個其它符號。漢字區的內碼範圍高字節從B0-F7,低字節從A1-FE,佔用的碼位是72*94=6768。其中有5個空位是D7FA-D7FE。GB2312-80中共收錄了7545個字符,用兩個字節編碼一個字符。每個字符最高位爲0。GB2312-80編碼簡稱國標碼。
GB2312支持的漢字太少。1995年的漢字擴展規範GBK1.0收錄了21886個符號,它分爲漢字區和圖形符號區。漢字區包括21003個字符。
4. UNICODE
Unicode字符集(簡稱爲UCS),國際標準組織於1984年4月成立ISO/IECJTC1/SC2/WG2工作組,針對各國文字、符號進行統一性編碼。1991年美國跨國公司成立UnicodeConsortium,並於1991年10月與WG2達成協議,採用同一編碼字集。目前Unicode是採用16位編碼體系,其字符集內容與ISO10646的BMP(BasicMultilingual Plane)相同。Unicode於1992年6月通過DIS(DrafInternationalStandard),目前版本V2.0於1996公佈,內容包含符號6811個,漢字20902個,韓文拼音11172個,造字區6400個,保留20249個,共計65534個。Unicode編碼後的大小是一樣的.例如一個英文字母"a" 和 一個漢字"好",編碼後都是佔用的空間大小是一樣的,都是兩個字節!
Unicode可以用來表示所有語言的字符,而且是定長雙字節(也有四字節的)編碼,包括英文字母在內。所以可以說它是不兼容iso8859-1編碼的,也不兼容任何編碼。不過,相對於iso8859-1編碼來說,uniocode編碼只是在前面增加了一個0字節,比如字母'a'爲"0061"。
需要說明的是,定長編碼便於計算機處理(注意GB2312/GBK不是定長編碼),而unicode又可以用來表示所有字符,所以在很多軟件內部是使用unicode編碼來處理的,比如java。
隨着國際互聯網的迅速發展,要求進行數據交換的需求越來越大,不同的編碼體系越來越成爲信息交換的障礙,而且多種語言共存的文檔不斷增多,單靠代碼頁已很難解決這些問題,於是UNICODE應運而生。
UNICODE 通常用作涉及雙字節字符編碼方案的通用術語。UNICODE CCS 3.1(CCS:編碼字符集,coded character set) 的官方稱謂是 ISO10646-1 通用多八字節編碼字符集(Universal Multiple Octet Coded Character Set,UCS)。
UNICODE 編碼系統可分爲編碼方式和實現方式兩個層次。
目前用於實用的 UNICODE 版本的編碼方式使用16位的編碼空間,也就是每個字符佔用2個字節。這樣理論上一共最多可以表示 65536 個字符,基本滿足各種語言的使用。標準的UNICODE編碼方案使用32位的的編碼空間,目前編碼範圍是0到10FFFF H。例如“啊”的UNICODE編碼爲55 4A H。
UNICODE 的實現方式不同於編碼方式。一個字符的 UNICODE 編碼是確定的。但是在實際傳輸過程中,由於不同系統平臺對非ASCII碼的處理都不太一致,以及出於節省空間的目的,對 UNICODE 編碼的實現有着不同的方式。UNICODE 的實現方式稱爲UNICODE轉換格式(UNICODE Translation Format,簡稱爲 UTF)。常用的有:
1)UTF-8, 8bit編碼, ASCII不作變換, 其他字符做變長編碼, 每個字符1-6 byte。其與UNICODE編碼的關係如下:
U-00000000-U-0000007F: 0xxxxxxx
U-00000080-U-000007FF: 110xxxxx 10xxxxxx
U-00000800-U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx
4-6位的UTF-8編碼對應着U-00010000-U-7FFFFFFF,比較少用到,就不列出了。
“啊”的UTF-8編碼是E5 95 8A,佔3字節。
2)UTF-16,16位編碼,基本上就是UNICODE編碼的實現。這裏只能說是“基本上”,因爲對於碼值在10000H到10FFFFH之間的 UNICODE碼,UTF-16是使用“代理對”(即兩個UNICODE碼)來實現編碼的。幸好,我們常用的字符,包括各國的文字、符號等,都可以使用單個UTF-16來編碼,不需要通過代理對實現。因此,UTF-16是UNICODE編碼實現的指定首選編碼方式。例如“啊”的UTF-16編碼是55 4A H,佔2字節,並且與實際“啊”的實用16位UNICODE編碼值是相等的。
3)UTF-32,32位編碼,顧名思義,與標準UNICODE編碼是一一對應的。例如“啊”的UTF-32編碼是00 00 55 4A,佔4字節。
上面羅列了一堆編碼,希望大家還沒有看暈過去。我們現在分析幾種JAVA中的亂碼問題。
首先,我們看看Window是如何處理文本文件的。Window2000內核是使用UNICODE編碼進行字符處理的。Window上的純文本編輯器(包括 JBuilder等JAVA開發工具的編輯器),在讀進一個純文本文件時,如果該文件本身沒有指定編碼格式,則編輯器會使用當前Window的代碼頁(中文Window下是CP936,即GBK)對文件內容字節流進行轉換,轉換成UNICODE編碼。在保存時,則將內存中的UNICODE編碼通過代碼頁轉換爲本地編碼,以字節流形式存入磁盤。
JAVA核心也使用UNICODE進行字符處理,與Window2000是一致的,因此,處理文件的過程也和上面是一樣的。因爲internet傳輸也是以字節流方式傳輸的,因此JAVA在處理網絡字節流時也按上述方式進行。在這種byte->char->byte類型的轉換中,如果中間出現編碼轉換錯誤,就會冒出中文亂碼問題了。
案例一:JAVA產生的文件(或控制檯輸出),在Window下正常,上傳到UNIX運行時亂碼。這是因爲兩臺機器的代碼頁不一致造成的。解決辦法:將UNIX上的語言區域設爲與Window的一致後,再運行JAVA程序。
案例二:文本文件使用Window文本編輯器可以正常打開,而JAVA應用程序讀入該文件時亂碼。原因是文本文件有多種的編碼方式包括ASCII,UTF- 8,UTF-16(而CPU有着字節順序的問題,故UTF-16又細分爲UTF-16LE小尾序、UTF-16BE大尾序兩種編碼)。文本文件在缺省下是根據當前系統的區域選定的編碼方式來保存的,Window下也就是代碼頁。解決辦法:檢查該文本文件的編碼方式,並在JAVA程序中按該編碼方式讀取數據。
5. UTF-8
在上面已經講述,其實是包含關係。
6. ISO8859-1:
屬於單字節編碼,最多能表示的字符範圍是0-255,應用於英文系列。比如,字母'a'的編碼爲0x61=97。
最常用的是ASCII編碼,編碼範圍是00 H-7F H。ISO8859編碼是對ASCII的擴展,擴展部分是80 H-FF H,用於定義其它非英文字母,如法語、德語等。其中ISO8859-1是最著名的一個,定義了西歐拉丁語符號。注意,ISO8859-1與GB18030 在編碼上也是重疊的。
很明顯,iso8859-1編碼表示的字符範圍很窄,無法表示中文字符。但是,由於是單字節編碼,和計算機最基礎的表示單位一致,所以很多時候,仍舊使用iso8859-1編碼來表示。而且在很多協議上,默認使用該編碼。比如,雖然"中文"兩個字不存在iso8859-1編碼,以gb2312編碼爲例,應該是"d6d0cec4"兩個字符,使用iso8859-1編碼的時候則將它拆開爲4個字節來表示:"d6d0 cec4"(事實上,在進行存儲的時候,也是以字節爲單位處理的)。而如果是UTF編碼,則是6個字節"e4b8 ad e6 96 87"。很明顯,這種表示方法還需要以另一種編碼爲基礎。
中文漢字編碼體系: