我們在開發過程中,特别是多種編碼格式并存的情況下,很容易遇到亂碼問題。 假如有一個GBK編碼java檔案,然後再使用-Dfile.encoding=GBK參數,寫入的檔案中哪些是亂碼呢。那如果使用UFT-8編碼的java檔案呢。
<a href="http://blog.51cto.com/13013670/1944050#">?</a>
<code>public</code> <code>class</code> <code>Main {</code>
<code> </code><code>static</code> <code>String content = </code><code>"中文"</code><code>;</code>
<code> </code><code>public</code> <code>static</code> <code>void</code> <code>main(String[] args) </code><code>throws</code> <code>IOException {</code>
<code> </code><code>OutputStreamWriter gbkWriter =</code><code>new</code> <code>OutputStreamWriter(</code><code>new</code> <code>FileOutputStream(</code><code>"GBK-FILE"</code><code>),</code><code>"GBK"</code><code>);</code>
<code> </code><code>// 情況1、2、5的結果肯定是一緻的</code>
<code> </code><code>gbkWriter.write(content+</code><code>"\n"</code><code>); </code><code>//(1)</code>
<code> </code><code>gbkWriter.write(</code><code>new</code> <code>String(content.getBytes(</code><code>"GBK"</code><code>),</code><code>"GBK"</code><code>)+</code><code>"\n"</code><code>); </code><code>// (2)</code>
<code> </code><code>gbkWriter.write(</code><code>new</code> <code>String(content.getBytes(</code><code>"GBK"</code><code>), </code><code>"UTF-8"</code><code>)+</code><code>"\n"</code><code>); </code><code>// (3)</code>
<code> </code><code>gbkWriter.write(</code><code>new</code> <code>String(content.getBytes(</code><code>"UTF-8"</code><code>),</code><code>"GBK"</code><code>)+</code><code>"\n"</code><code>); </code><code>// (4)</code>
<code> </code><code>gbkWriter.write(</code><code>new</code> <code>String(content.getBytes(</code><code>"UTF-8"</code><code>), </code><code>"UTF-8"</code><code>)+</code><code>"\n"</code><code>); </code><code>// (5)</code>
<code> </code><code>gbkWriter.flush();</code>
<code> </code><code>gbkWriter.close();</code>
<code> </code><code>}</code>
<code>}</code>
其實用一張圖就可以清晰的概括出從java檔案編譯到輸出的過程 主要有3個地方的編碼轉換:
上圖①所示的位置,其實就是
javac -encoding xxx
的時候控制,如果你沒有顯示的指定編碼,那麼會根據目前作業系統的預設編碼格式進行編譯,一般windows是gbk,linux是UTF-8。如果這裡編碼指定錯了,那麼你的代碼很有可能出現中文亂碼問題,注意是很有可能,而不是絕對。原因後面會說到。編譯出來的class檔案統一都是UTF-8格式
當class檔案加載的jvm的時候,也會進行字元串編碼轉換,和前面一樣,會使用作業系統預設的編碼格式,這裡的編碼不是指class檔案的編碼,而是指java檔案的編碼格式,類似于指定java檔案是什麼編碼格式編譯為class檔案的。就是jvm參數:
java -Dfile.encoding=xxx
在java運作過程中,字元串在記憶體中則是使用Unicode(UTF-16)進行存儲的。而UTF-8轉換為UTF-16是很簡單的過程。當我們标用String.getBytes()時候,則是把記憶體中的unicode轉換對應的位元組數組。(如果沒有指定,則使用作業系統預設的編碼格式)。可以看出,把一個字元串從編譯到記憶體,其實是經曆的過程為:
編譯檔案編碼->加載jvm編碼->unicode
輸出的編碼則是在代碼中指定的。例如: OutputStreamWriter gbkWriter =new OutputStreamWriter(new FileOutputStream("GBK-FILE"),"GBK");
如果了解上面的,我們再看看文章一開始的例子。舉幾個例子做說明,其他的情況也就逐類旁通。
java檔案編碼:GBK,
javac -encoding GBK
java -Dfile.encoding=GBK 那麼使用GBK編碼檢視輸出檔案
(1)正常
(2)正常
(3)亂碼
(4)亂碼
(5)正常
情況1是比較好了解的,因為java檔案編碼、編譯、加載都是使用GBK,加載到記憶體中Unicode肯定也是正常的,那麼列印出來也是正常的。
在情況1的前提下(即加載到記憶體中是正常的),在jvm中使用GBK解碼在編碼肯定是正常的。
在情況1的前提下,使用不同的解碼和編碼,肯定是亂碼
當我們使用UTF-8的格式打開檔案的時候,情況(4)是正常的,其餘都是亂碼。其實是因為先使用unicode進行轉換為UTF-8格式的Byte數組,生成的字元串雖然亂碼和寫檔案的格式都是GBK,相當于原封不動的UTF-8格式的byte數組寫到檔案中,是以就會出現這個情況
這個例子就是直至加載到記憶體都是正常的情況下,在jvm内進行編碼和解碼導緻亂碼的情況
java檔案編碼:UTF-8,
java -Dfile.encoding=UTF-8
(1)亂碼
(2)亂碼
(3)正常
(5)亂碼
情況1是亂碼,說明字元串加載到記憶體中就已經是亂碼了。因為UFT-8格式使用GBK進行編碼,在生成class檔案就已經是亂碼了。
情況3為什麼又是正常的呢,其實這是誤打誤撞類型。個人了解的造成這個情況的原因有:
java檔案的編碼正好和jvm加載檔案編碼格式是一樣的
javac過程,相當于一個UTF-8->GBK格式轉換,而content.getBytes("GBK"), "UTF-8")又相當于GBK->UTF-8的轉換,兩次轉換正好互相抵消。
為什麼是使用UTF-8打開情況1不是亂碼了呢,其實和上面誤打誤撞,隻不過之前發生在記憶體中的GBK->UTF-8換為我們在打開檔案的時候進行的編碼轉換,情況1、2、5結果肯定一緻的
本文轉自 bxst 51CTO部落格,原文連結:http://blog.51cto.com/13013670/1944050