java中幾種常見字符集與亂碼介紹


1.  ASCII和Ansi編碼

      字符內碼(charcter code)指的是用來代表字符的內碼

 

.讀者在輸入和存儲文檔時都要使用內碼,內碼分為

    單字節內碼 -- Single-Byte character sets

 

(SBCS),可以支持256個字符編碼.

    雙字節內碼 -- Double-Byte character sets)

 

(DBCS),可以支持65000個字符編碼.

前者即為ASCII編碼,后者對應ANSI.

至於簡體中文編碼GB2312,實際上它是ANSI的一個代

 

碼頁936

 

2.Unicode符號集

 

正如上一節所說,世界上存在着多種編碼方式,同一個二

 

進制數字可以被解釋成不同的符號。因此,要想打開一個

 

文本文件,就必須知道它的編碼方式,否則用錯誤的編碼

 

方式解讀,就會出現亂碼。為什么電子郵件常常出現亂碼

 

?就是因為發信人和收信人使用的編碼方式不一樣。而

 

Unicode就是這樣一種編碼:它包含了世界上所有的符號,

 

並且每一個符號都是獨一無二的。比如,U+0639表示阿拉

 

伯字母Ain,U+0041表示英語的大寫字母A,U+4E25表示漢

 

字“嚴”。具體的符號對應表,可以查詢unicode.org,或

 

者專門的漢字對應表 。很多人都說Unicode編碼,但其實

 

Unicode是一個符號集(世界上所有符號的符號集),而不

是一種新的編碼方式。

 

但是正因為Unicode包含了所有的字符,而有些國家的字符

 

用一個字節便可以表示,而有些國家的字符要用多個字節

 

才能表示出來。即產生了兩個問題:第一,如果有兩個字

 

節的數據,那計算機怎么知道這兩個字節是表示一個漢字

 

呢?還是表示兩個英文字母呢?第二,因為不同字符需要

 

的存儲長度不一樣,那么如果Unicode規定用2個字節存儲

 

字符,那么英文字符存儲時前面1個字節都是0,這就大大

 

浪費了存儲空間。

 

上面兩個問題造成的結果是:1)出現了unicode的多種存

 

儲方式,也就是說有許多種不同的二進制格式,可以用來

 

表示unicode。2)unicode在很長一段時間內無法推廣,直

 

到互聯網的出現。

 

3.UTF-16

說到 UTF 必須要提到 Unicode(Universal Code 統一碼

 

),ISO 試圖想創建一個全新的超語言字典,世界上所有

 

的語言都可以通過這本字典來相互翻譯。可想而知這個字

 

典是多么的復雜,關於 Unicode 的詳細規范可以參考相應

 

文檔。Unicode 是 Java 和 XML 的基礎,下面詳細介紹

 

Unicode 在計算機中的存儲形式。

UTF-16 具體定義了 Unicode 字符在計算機中存取方法。

 

UTF-16 用兩個字節來表示 Unicode 轉化格式,這個是定

 

長的表示方法,不論什么字符都可以用兩個字節表示,兩

 

個字節是 16 個 bit,所以叫 UTF-16。UTF-16 表示字符

 

非常方便,每兩個字節表示一個字符,這個在字符串操作

 

時就大大簡化了操作,這也是 Java 以 UTF-16 作為內存

 

的字符存儲格式的一個很重要的原因。

 

4.UTF-8

UTF-16 統一采用兩個字節表示一個字符,雖然在表示上非

 

常簡單方便,但是也有其缺點,有很大一部分字符用一個

 

字節就可以表示的現在要兩個字節表示,存儲空間放大了

 

一倍,在現在的網絡帶寬還非常有限的今天,這樣會增大

 

網絡傳輸的流量,而且也沒必要。而 UTF-8 采用了一種變

 

長技術,每個編碼區域有不同的字碼長度。不同類型的字

 

符可以是由 1~4 個字節組成。

UTF-8 有以下編碼規則:

如果一個字節,最高位(第 8 位)為 0,表示這是一個

 

ASCII 字符(00 - 7F)。可見,所有 ASCII 編碼已經是

 

UTF-8 了。

如果一個字節,以 11 開頭,連續的 1 的個數暗示這個字

 

符的字節數,例如:110xxxxx 代表它是雙字節 UTF-8 字

 

符的首字節。

 

5.GBK/GB2312/GB18030

 

GBK和GB2312都是針對簡體字的編碼,只是GB2312只支持六

 

千多個漢字的編碼,而GBK支持1萬多個漢字編碼。而

 

GB18030是用於繁體字的編碼。漢字存儲時都使用兩個字節

 

來儲存。

 

總的來說:

 

ASCII編碼:用來表示英文,它使用1個字節表示,其中第

 

一位規定為0,其他7位存儲數據,一共可以表示128個字符

 

 

拓展ASCII編碼:用於表示更多的歐洲文字,用8個位存儲

 

數據,一共可以表示256個字符

 

GBK/GB2312/GB18030:表示漢字,用兩個字節表示。

 

GBK/GB2312表示簡體中文,GB18030表示繁體中文。

 

Unicode編碼:包含世界上所有的字符,是一個字符集。

 

UTF-8:是Unicode字符的實現方式之一,它使用1-4個字節

 

表示一個符號,根據不同的符號而變化字節長度。

 

UTF-16:

UTF-8:是Unicode字符的實現方式之一,它使用2個字節表

 

示一個符號

 

關於亂碼:

       亂碼問題的產生最根本的原因就是使用錯誤的字符集解碼字節流或者將給定的字符串用錯誤的字符集編碼成錯誤字節流造成的,例如”中文”兩個漢字,如果用ISO8859-1字符集將其編碼為字節流,因為這個字符集不支持中文,所以就會出錯,輸出結果為3f3f,其意義就是??。再例如”中文”二字的GBK的字節流為d6 d0 ce c4,可是我們要是用不兼容的字符集去解碼,例如用ISO8859-1或者UTF-8,這隨后產生的字符串就是亂碼,或者是其他的某個字符。

解決java編程中出現亂碼的方法:

從開發Java程序到運行Java程序的過程中都存在着編碼問題,所以要想避免亂碼產生,就必須了解在其中任何時候的編碼處理的情況。

 

1、源代碼:在編寫java源代碼的時候,我們必須把編寫的文本保存在文件中,這個時候不管用什么編輯器,都存在一個問題,就是以什么樣的字符集將這些源代碼(包含漢字)保存到文件中,大部分編輯器都會通過系統的環境變量得到系統的當前默認字符集,編輯器就會使用這個字符集將我們編寫的源代碼保存到文件中。一般我們的中文Windows系統的默認字符集是GB18030,AIX英文環境的默認字符集是ISO8859-1,AIX中文環境的默認字符集是IBM-eucCN。

 

2、編譯:在編譯.java文件的時候如果使用默認處理,則javac會使用系統當前的默認字符集去讀取源文件,將源文件的內容轉換為UTF-8編碼,然后在進行編譯,這時我們也可以通過-encoding參數指定一個字符集,讓javac使用我們指定的字符集去讀取源代碼然后在轉換為UTF-8,然后編譯。編譯以后產生的class文件內部所有的中文字符都是用UTF-8的字符集進行編碼的,這就是Java程序能處理任何國家文字的原因。

 

3、運行時:Java程序在運行時,需要使用程序內部定義的中文字符串,也可能會使用從外部讀取的中文字符串,這些經過處理,可能都會輸出到程序外部,在這些 過程中都涉及到編碼的轉換,程序內部定義的字符串都是用UTF-8存儲的。而從外部讀取和輸出到程序外部的輸出又使用什么字符集進行處理呢?在我們沒有在 程序中特別指定的情況下,JVM會根據系統屬性確定使用哪個字符集,這個系統屬性的名稱為file.encoding,我們可以在啟動java程序的時候通過-D參數設定這個值,如果沒有設定,JVM會根據系統環境變量確定這個系統屬性,一般我們的中文Windows系統的默認字符集是GB18030,AIX英文環境的默認字符集是ISO8859-1,AIX中文環境的默認字符集是IBM-eucCN。這樣JVM在處理輸入數據的時候就會把字節流根據這個參數進行解碼,然后轉成UTF-8格式,在Java程序內部處理,然后再根據這個參數把處理后的數據編碼,輸出到程序外部。這就是Java程序運行時字符集的使用情況。

 

現在有一個問題,我們平時都是Windows的中文環境下做開發,然后拿到AIX系統上去運行,AIX系統的默認語言環境是英文環境,這樣就會出現亂碼,分析過程如下:源文件編碼格式為GB18030,默認編譯,也采用GB18030讀取源文件,正常轉換為UTF-8,生成class文件,運行時沒有進行特殊設置,語言環境為英文環境,默認編碼為ISO8859-1,這樣在輸出中文的時候會把正常的UTF-8表示的漢字用ISO8859-1的字符集去編碼生成字節流,因為ISO8859-1不支持漢字,結果輸出的都是’?’。可是這個時候卻發現,由外界輸入給java程序的中文字符,卻能正常輸出,這又是為什么,其實這個也是運行時的默認字符集ISO8859-1造成的,Java程序運行時,在讀取外部進入的字節流的時候,如果使用默認的讀取方式,也是使用ISO8859-1的字符集進行解碼處理,這樣中間的處理過程中,中文都已經不是原來的中文了,也就是說我們這個時候處理根本不是我們認為的中文,而是一對亂碼,雖然是亂碼,但是其中的信息卻沒有丟失,在處理完后,在經過一次ISO8859-1的編碼,又還原為正常的GB18030的編碼輸出,所有沒有出現亂碼。我們以前的解決方法是,在編譯原文件的時候指定參數-encoding ISO8859-1,讓編譯器用ISO8859-1的字符集去解碼源文件編譯,然后運行程序,這時再輸出程序的內部中文字符串也不是亂碼了。看起來一切都解決了,可是卻沒有從根本上解決問題,class文件變得比平常大很多,程序中用到中文越多,class文件變大的越快。而且其中的中文信息也變味了。

 

另一個問題,如果我們正常編譯程序,在AIX系統上線設定為中文環境,然后再運行Java程序,這樣既不會使程序變大,也不會使中文變味,可是用了一段時間又發現問題了,處理過程中如果遇到偏僻的中文字,還是亂碼,原因是AIX的中文環境使用的字符集是IBM-eucCN,我認為可能是這個字符集缺少偏僻漢字,無法解釋其內容,所以偏僻字變成了亂碼了。

 

最后的解決辦法是,在Windows中文環境下正常編寫原程序,用默認的方式編譯生成class文件,或者編譯時指定參數-encoding GB18030,這樣漢字都能正常解釋並轉換為UTF-8存儲在class文件中,在運行的時候,我們需要制定參數,java –Dfile.encoding=GB18030 。。。。。,系統環境使用默認英文即可,這樣JVM就不會根據系統的環境設定默認字符集,而是所有輸入輸出都使用我們指定的字符集,這樣不但解決了英文環境下的中文輸出問題,而且還解決了偏僻字的顯示問題。

 

         結論是,如果是源代碼亂碼,原因是保存源代碼的時候的編碼方式與打開源代碼的時候的編碼方式不一致,這時只需要轉換編輯器的編碼方式與保存時候的編碼方式一致即可解決亂碼問題;如果是編譯的時候出現亂碼,javac會使用系統當前的默認字符集去讀取源文件,所以需要將編碼方式改為系統默認的編碼方式;如果是運行時產生亂碼,就需要在運行java程序的時候加上指令java –Dfile.encoding= **(運行平台的默認字符集)即可

 

例子:

 

源代碼亂碼

保存時編碼方式為:ANSI

 

 

將編碼方式改為utf-8時,源代碼出現亂碼

 

 

 

但這時候編譯正常通過並沒有問題

 

 

原因是雖然修改編碼方式,但是並沒有改變源代碼保存時候的編碼方式,他仍然是以系統

默認的編碼方式保存的,雖然在編輯器上看起來亂碼,但在編譯的時候以系統默認的字符集去編譯是不會產生亂碼的。

 

編譯亂碼:

上面例子如果我轉換了編碼方式,把他轉為utf-8編碼

其編譯就會出錯。

 

 

轉回ANSI又能編譯通過

 

 

 運行時異常,由於筆者手頭上沒有別的運行系統了,就不再示范,請讀者自行試驗。

 

 

關於io操作時候的亂碼現象,也是由於編碼方式與解碼方式不同造成的,下面給出幾個例子

 

此例程是以utf8的編碼方式寫進文件,以utf16的解碼方式讀取文件,導致輸出亂碼:

 

 

 

如果改回utf8的解碼方式:

 

 

 

顯示正常,同理,如果寫入時候的編碼方式不一樣,也會造成亂碼:

 

以utf16的編碼方式讀取的test再以utf8的編碼方式寫進test2,結果造成亂碼

 

還有其他一些例子不再一一列子,讀者請自行嘗試。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM