一、字節流讀取中文時出現的問題:
?文件中有中文時,用字節流讀取會出現亂碼的問題,因為一個中文為兩個字節。
二、字符編碼表
編碼表:其實就是生活中字符和計算機二進制的對應關系表。
1、ascii: 一個字節中的7位就可以表示。對應的字節都是正數。0-xxxxxxx
2、iso-8859-1:拉丁碼表 latin,用了一個字節用的8位。1-xxxxxxx? 負數。
3、GB2312:簡體中文碼表。包含6000-7000中文和符號。用兩個字節表示。兩個字節第一個字節是負數,第二個字節可能是正數
? ??? GBK:目前最常用的中文碼表,2萬的中文和符號。用兩個字節表示,其中的一部分文字,第一個字節開頭是1,第二字節開頭是0
?? ?? GB18030:最新的中文碼表,目前還沒有正式使用。
4、unicode:國際標準碼表:無論是什么文字,都用兩個字節存儲。
? ? ? Java中的char類型用的就是這個碼表。char c = 'a';占兩個字節。
????? Java中的字符串是按照系統默認碼表來解析的。簡體中文版 字符串默認的碼表是GBK。
5、UTF-8:基于unicode,一個字節就可以存儲數據,不要用兩個字節存儲,而且這個碼表更加的標準化,在每一個字節頭加入了編碼信息(后期到api中查找)。
能識別中文的碼表:GBK、UTF-8;正因為識別中文碼表不唯一,涉及到了編碼解碼問題。
對于我們開發而言,常見的編碼 GBK? UTF-8? ISO-8859-1
文字--->(數字) :編碼。 “abc”.getBytes()? byte[]??? //數字就是字節
(數字)--->文字? : 解碼。 byte[] b={97,98,99}? new String(b) ?用new String 傳一個字節數組
三、字符輸入流? Reader類
Reader類:讀取字符流的超類。
方法:
1.read():讀取單個字符并返回
2.read():將數據讀取到數組中,并返回讀取的個數
四、字符輸入類 FileReader類
Reader類的子類,用來讀取字符文件的便捷類。此類的構造方法假定默認字符編碼和默認字節緩沖區大小都是適當的。
?構造方法:FileReader fr=new FileReader(文件路徑/文件對象);
package com.oracle.demo01; import java.io.FileReader; import java.io.IOException; //字符輸入流 public class ReaderDemo {//因為是一個字符一個字符讀取的,所以讀取中文不會出現亂碼,專門用來處理文本文件public static void main(String[] args) throws IOException {// 1.獲取數據源FileReader fr=new FileReader("d:\\java\\writer.txt");//2.讀取數據int len=0;while((len=fr.read())!=-1){System.out.println((char)len);}fr.close();}}
?
?五、字符輸出流 Writer類
Writer是寫入字符流的抽象類。其中描述了相應的寫的動作。
?方法:
六、字符輸出類 FileWriter類
Writer類的子類,用來寫入字符文件的便捷類。此類的構造方法假定默認字符編碼和默認字節緩沖區大小都是可接受的。
?構造方法:FileWriter fw=new FileWriter(文件路徑/文件對象);
package com.oracle.demo01;import java.io.FileWriter; import java.io.IOException; //字符輸出流 public class WriteDemo {//字符流處理的是文本文件public static void main(String[] args) throws IOException {// 文本文件:打開人能看懂的就是文本文件//1.確定目的地FileWriter fw=new FileWriter("d:\\java\\writer.txt",true);//2.寫入數據//fw.write(100); // 如果不刷新,會保存在內存中 // char[] ch={'猴','賽','雷'}; //字符數組 // fw.write(ch,0,2); //可以輸出字符數組,也可以控制輸出的長度fw.write("崔永元實話實說",0,3); //單引號是 字符,雙引號是字符串//3.刷新 fw.flush();//4.釋放資源 fw.close();}}
?
?flush()方法與colse()方法的區別:
flush():將流中的緩沖區緩沖的數據刷新到目的地中,刷新后,流還可以繼續使用,還可以繼續輸出。
close():關閉資源,但在關閉前會將緩沖區中的數據先刷新到目的地,否則丟失數據,然后再關閉流。關閉后流不可以使用。
注意:如果寫入數據多,一定要一邊寫一邊刷新,最后一次可以不刷新,由close完成刷新并關閉。
?練習題:用字符流復制文件
?
package com.oracle.demo01;import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; //用字符流復制文本文件,不要用來復制圖片與視頻等 public class Copy {public static void main(String[] args) throws IOException {// 1.獲取數據源FileReader fr=new FileReader("e:\\java\\outint.PNG");//2.獲取目標FileWriter fw=new FileWriter("D:\\java\\outin.png");//3.讀取并寫入int len =0;char[] ch=new char[1024];while((len=fr.read(ch))!=-1){fw.write(ch,0,len);}fw.flush();fr.close();fw.close();}}
?七、轉換流
?如果需要指定編碼和緩沖區大小時,可以在字節流的基礎上,構造一個InputStreamReader或者OutputStreamWriter。比如,如果文件內為UTF-8格式,但是電腦系統默認的格式為GBK格式,直接讀取的話會出現讀取內容與文件內容不符的問題,這個時候就需要轉換流來轉換。
7.1輸出轉換類OutputStreamWriter類
?
OutputStreamWriter 是字符流通向字節流的橋梁:可使用指定的字符編碼表,將要寫入流中的字符編碼成字節。它的作用的就是,將字符串按照指定的編碼表轉成字節,再使用字節流將這些字節寫出去。
?
package com.oracle.demo01;import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; //轉換流 輸出 public class OutputStreamDemo {//結尾為Streatm的為字節流 結尾為reader或writer的為字符流public static void main(String[] args) throws IOException {//1.確定目的地//字節流輸出對象FileOutputStream fos=new FileOutputStream("e:\\java\\writer.txt",true);//2.設置走哪個碼表 碼表是文本文件內容所使用的碼表OutputStreamWriter osw=new OutputStreamWriter(fos,"utf-8");//3.寫入數據//寫入時就轉換為了指定碼表的字符流osw.write("野豬佩奇");osw.flush();osw.close();}}
?
OutputStreamWriter流對象,它到底如何把字符轉成字節輸出的呢?
其實在OutputStreamWriter流中維護自己的緩沖區,當我們調用OutputStreamWriter對象的write方法時,會拿著字符到指定的碼表中進行查詢,把查到的字符編碼值轉成字節數存放到OutputStreamWriter緩沖區中。然后再調用刷新功能,或者關閉流,或者緩沖區存滿后會把緩沖區中的字節數據使用字節流寫到指定的文件中。
7.2轉換輸入流 InputStreamReader類
InputStreamReader 是字節流通向字符流的橋梁:它使用指定的字符編碼表讀取字節并將其解碼為字符。它使用的字符集可以由名稱指定或顯式給定,或者可以接受平臺默認的字符集。
package com.oracle.demo01;import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader;public class InputStreamDemo { //轉換流 先走碼表,根據碼表解析,然后再進行讀寫操作public static void main(String[] args) throws IOException {//1.獲取數據源FileInputStream fis=new FileInputStream("e:\\java\\writer.txt");//2.設置走哪個碼表InputStreamReader isr=new InputStreamReader(fis,"utf-8");//3.讀取數據int len=0;while((len=isr.read())!=-1){System.out.print((char)len);}isr.close();}}
?
注意:在讀取指定的編碼的文件時,一定要指定編碼格式,否則就會發生解碼錯誤,而發生亂碼現象。
?
7.3轉換流和字符類區別
發現有如下繼承關系:
父類:OutputStreamWriter:
????????????????? 子類:FileWriter:
父類:InputStreamReader:
子類:FileReader;
OutputStreamWriter和InputStreamReader是字符和字節的橋梁:也可以稱之為字符轉換流。字符轉換流原理:字節流+編碼表。
FileWriter和FileReader:作為子類,僅作為操作字符文件的便捷類存在。當操作的字符文件,使用的是默認編碼表時可以不用父類,而直接用子類就完成操作了,簡化了代碼。
InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"));//默認字符集。
InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"),"GBK");//指定GBK字符集。
FileReader fr = new FileReader("a.txt");
這三句代碼的功能是一樣的,其中第三句最為便捷。
注意:一旦要指定其他編碼時,絕對不能用子類,必須使用字符轉換流。什么時候用子類呢?
條件:
1、操作的是文件。2、使用默認編碼。
總結:
字節--->字符 : 看不懂的--->看的懂的。? 需要讀。輸入流。 InputStreamReader
字符--->字節 : 看的懂的--->看不懂的。? 需要寫。輸出流。 OutputStreamWriter
練習題:
?
package com.oracle.demo01;import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; //轉換流復制文本文件 public class CopyTwo {public static void main(String[] args) throws IOException {FileInputStream fis=new FileInputStream("e:\\java\\writer.txt");InputStreamReader osr=new InputStreamReader(fis,"utf-8");FileOutputStream fos=new FileOutputStream("d:\\java\\writer.txt");OutputStreamWriter osw=new OutputStreamWriter(fos,"utf-8");int len=0;while((len=osr.read())!=-1){osw.write(len);}osr.close();osw.flush();osw.close();}}
?
?
?