Java中的字符集編碼入門(五)

 

若是你是JVM的設計者,讓你來決定JVM中全部字符的表示形式,你會不會容許使用各類編碼方式的字符並存?小程序

 

我想你的答案是不會,若是在內存中的Java字符能夠以GB2312UTF-16BIG5等各類編碼形式存在,那麼對開發者來講,連進行最基本的字符串打印、鏈接等操做都會步履維艱。例如一個GB2312的字符串後面鏈接一個UTF-8的字符串,那麼鏈接後的最終結果應該是什麼編碼的呢?你選哪個都沒有道理。編碼

 

所以牢記下面這句話,這也是Java開發者的共贊成志:在Java中,字符只以一種形式存在,那就是Unicode(注意到咱們沒有選擇特定的編碼,直接使用它們在字符集中的編號,這是統一的惟一方法)。spa

 

但「在Java中」究竟是指在哪裏呢?就是指在JVM中,在內存中,在你的代碼裏聲明的每個charString類型的變量中。例如你在程序中這樣寫設計

 

char han='';code

 

 

 

在內存的相應區域,這個字符就表示爲0x6C49.能夠用下面的代碼證實一下:orm

 

char han='';視頻

System.out.format("%x",(short)han);內存

 

 

 

輸出是:6c49反過來用Unicode編號來指定一個字符也能夠,像這樣:utf-8

 

char han=0x6c49;ci

System.out.println(han);

 

 

 

輸出是:漢

 

這其實也是說,只要你正確的讀入了「漢」這個字,那麼它在內存中的表示形式必定是0x6C49,沒有任何其餘的值能表明這個字(固然,若是你讀錯了,那結果是什麼就不知道了,範偉說:讀,讀錯了呀,那還等於好幾億呢;本山大哥說:好幾億你也沒答上,請聽下一題)。

 

JVM的這種約定使得一個字符存在的世界分爲了兩部分:JVM內部和OS的文件系統。在JVM內部,統一使用Unicode表示,當這個字符被從JVM內部移到外部(即保存爲文件系統中的一個文件的內容時),就進行了編碼轉換,使用了具體的編碼方案(也有一種很特殊的狀況,使得在JVM內部也須要轉換,不過這個是後話)。

 

所以能夠說,全部的編碼轉換就只發生在邊界的地方,JVMOS的交界處,也就是你的各類輸入輸出流(或者ReaderWriter類)起做用的地方。

 

話頭扯到這裏就必須接着說JavaIO系統。

 

儘管看上去混亂繁雜,可是全部的IO基本上能夠分爲兩大陣營:面向字符的ReaderWrtier啊,以及面向字節的輸入輸出流。

 

下面我來逐一分解,其實一點也不難。

 

面向字符和麪向字節中的所謂「面向」什麼,是指這些類在處理輸入輸出的時候,在哪一個意義上保持一致。若是面向字節,那麼這類工做要保證系統中的文件二進制內容和讀入JVM內部的二進制內容要一致。不能變換任何01的順序。所以這是一種很是「忠實於原著」的作法(偶然間讓我想起郭敬明抄襲莊羽的文章,那傢伙,太忠實於原著了,笑)。

 

這種輸入輸出方式很適合讀入視頻文件或者音頻文件,或者任何不須要作變換的文件內容。

 

而面向字符的IO是指但願系統中的文件的字符和讀入內存的「字符」(注意和字節的區別)要一致。例如咱們的中文版WindowsXP系統上有一個GBK的文本文件,其中有一個「漢」字,這個字的GBK編碼是0xBABA(而Unicode編號是0x6C49),當咱們使用面向字符的IO把它讀入內存並保存在一個char型變量中時,我但願IO系統不要傻傻的直接把0xBABA放到這個char型變量中,我甚至都不關心這個char型變量具體的二進制內容究竟是多少,我只但願這個字符讀進來以後仍然是「漢」這個字。

 

從這個意義上也能夠看出,面向字符的IO類,也就是ReaderWriter類,實際上隱式的爲咱們作了編碼轉換,在輸出時,將內存中的Unicode字符使用系統默認的編碼方式進行了編碼,而在輸入時,將文件系統中已經編碼過的字符使用默認編碼方案進行了還原。我兩次提到「默認」,是說ReaderWriter的聰明也僅此而已了,它們只會使用這個默認的編碼來作轉換,你不能爲一個Reader或者Writer指定轉換時使用的編碼。這也意味着,若是你使用中文版WindowsXP系統,而上面存放了一個UTF-8編碼的文件,當你使用Reader類來讀入的時候,它會傻傻的使用GBK來作轉換,轉換後的內容固然驢脣不對馬嘴!

 

這種笨,有時候實際上是一種傻瓜式的功能提供方式,對大多數初級用戶(以及不須要跨平臺的高級用戶)來講反而是件好事。

 

但咱們不同啦,咱們都是國家棟梁,肩負着趕英超美的責任,必須師夷長技以治夷,因此咱們總還要和GBK編碼之外的文件打交道。

 

說了上面這些內容,想必聰明的讀者已經看出來,所謂編碼轉換就是一個字符與字節之間的轉換,所以JavaIO系統中可以指定轉換編碼的地方,也就在字符與字節轉換的地方,那就是(讀者:InputStreamReaderOutputStreamWriter!做者:太強了,都會搶答了!)

 

這兩個類是字節流和字符流之間的適配器類,所以他們肩負着編碼轉換的任務簡直太天然啦!要注意,實際上也只能在這兩類實例化的時候指定編碼,是否是很好記呢?

 

下面來寫一段小程序,來把「漢」字用咱們很是崇拜的UTF-8編碼寫到文件中!

 

try{

PrintWriter out=new PrintWriter(new OutputStreamWriter(new FileOutputStream("c:/utf-8.txt"),"UTF-8"));

try{

out.write("");

}finally{

out.close();

}

}catch(IOException e){

throw new RuntimeException(e);

}

 

 

 

運行以後到c盤下去找utf-8.txt這個文件,用UltraEdit打開,使用16進制查看,看到了什麼?它的值是0xE6B189!噢耶!(讀者:這,這有什麼好高興的……)

相關文章
相關標籤/搜索