java中IO及中文亂碼問題


第一次發這種博客,所以一直在糾結怎么開頭,干脆直奔主題吧,把自己的一些總結體會記錄下來,先從簡單的學習開始,希望可以慢慢堅持,以后的內容可以寫的更好更有意義。

其實真正在工作中中文亂碼的問題遇到的不多,那是因為公司為了開發方便所以文件都統一編碼了。但是我覺得還有很有必要去稍微了解一下其原理的。

IO就是輸入輸出流,用面向對象來理解的話,就是輸入,輸出流對象,主要用來操作文件對象。所以再稍微談談文件的概念,即File對象。在Java中,File不是我們平時生活中指的某個具體文件,而是某個路徑對象,比如說 File file=new File("D:\\aaa"); 這就是一個File對象,也許它表示的是一個文件夾,也許這個路徑都不存在,但是這句代碼就的的確確創建了一個表示該路徑的File對象。這種寫法只是不常用罷了。因為我們平時可能更多的是操作一個文本,圖片等等,如File f=new File("aaa.txt");

上面簡單說了IO,File是什么,接下來再談談這些圖片,文字,視頻等信息是如何保存在我們存儲設備上的。個人理解是不管是什么類型的文件,都是二進制的形式保存,最小單位是1個byte,即8位01組成。所以說我們假如要拷貝一個文件,只要操作字節流就好了,即把一個文件中的所有字節拿到,寫到另一個文件中就OK了,其實理論上是可以的,但是對於字符型的文件比較特殊。這也就是為什么會有中文亂碼的問題出現。ASCII碼表大家都很熟悉,起碼都聽說過,它應該算是很早出現的一種碼表了吧,起初只是用來表示26個英文字母和一些特殊符號(因為計算機只識別二進制,所以要把字符用相應的字節來代替,形成一張碼表)。但是隨着計算機的發展,ASCII應該不夠用了吧,而且很多國家應該也都有自己的一套編碼方案,所以就出現了不同的編碼表。常見的有GBK,UTF-8,而jvm中默認使用的是unicode編碼,即以2個字節表示一個漢字,UTF-8則不一定,可能3個字節表示一個漢字,也可能更多。所以就出現了一個問題,同一個漢字在不同的碼表中對應的字節碼的個數和內容都不相同。所以如何解決?

我們從A磁盤上拷貝一張圖片到B磁盤上,只要把A的所有字節拿到B就可以了。但是同樣的方式操作一個文本其實也是可以的,前提是A和B中的文本編碼要相同。因為圖片不存在字節編碼的問題。但是我要從網絡上或者服務器去傳輸中文怎么辦呢,肯定不能單單通過字節來實現了(因為我們不可能遇到問題就手動去更改文件的編碼方式吧)。所以java中提供了字符流對象,即在字節流的基礎上加上對編碼的設置,達到解決亂碼的問題。

廢話不多說,用幾個小案例來說明一下:

1,首先在當前項目下新建aa.txt,bb.txt。隨便在aa中寫幾個中文字符。會發現這兩種方式都可以實現

a,采用字符流

FileReader fr=new FileReader("aa.txt");
FileWriter fw=new FileWriter("bb.txt");
  int c;
  while((c=fr.read())!=-1){
   fw.write(c);
  }
  fr.close();
  fw.close();

b,采用字節流

FileInputStream fis=new FileInputStream("aa.txt");
FileOutputStream fos=new FileOutputStream("bb.txt");
  int b;
  while((b=fis.read())!=-1){
   fos.write(b);
  }
  fis.close();
  fos.close();

2,此時假如aa的編碼方式是UTF-8的話,那么我們把bb的編碼改為GBK看看,同樣運行上面兩種方法,全部亂碼。

原因就是由於兩個文件的編碼方式不同,導致中文查的碼表不同,所以亂碼。

3,所以當兩邊文件的編碼方式不同,我們可以在讀取和寫入的時候都指定與其文件對應的編碼即可。

實現方式如下:

InputStreamReader isr=new InputStreamReader(new FileInputStream("aa.txt"),"utf-8");
OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("bb.txt"),"gbk");
  char[] arr=new char[1024];
  int len;
  while((len=isr.read(arr))!=-1){
   String s=new String(arr,0,len);
   System.out.println(s);
   osw.write(s);
  }
  isr.close();
  osw.close();

代碼雖然很簡單,還是簡單的解釋一下,從API中可以發現InputStreamReader和OutputStreamWriter都是操作字符的對象,繼續Reader和Writer。

主要用來把字節轉成字符,字符轉成字節。所以從構造中也可以發現,傳入的是字節流對象。以utf-8去讀取字節流轉成字符,再將字符以gbk編碼轉成字節寫入。

下面幾行就不做解釋了,都是基礎里面的方法。構造中傳入的是匿名內部類對象,還有裝飾設計模式,這種寫法簡單了解下即可。

關於字節流和字符流其實還有很多很好用的類,比如BufferedInputStream,BufferedReader等等,再次不做贅述。

關於jvm和系統平台的編碼問題,在次不做解釋。

可以用String來嘗試一下,觀察字符串在編譯和運行時的字節碼及編碼問題。

 


免責聲明!

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



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