前面一章介紹了字節流的使用,提到了字節流在處理 utf-8 編碼的中文可能會出現亂碼的情況(其他編碼的中文同樣會出現亂碼),所以Java針對這一情況提供了字符流。
但是字符流只能處理字符,不能用來處理 .jpg;.mp3;.mp4;.avi;.doc;.ppt等二進制文件,這些只能通過字節流來處理。所以對於純文本的文件,強烈推薦使用字符輸入輸出流。
字符流的本質其實就是基於字節流在讀取時去查了指定的碼表。
1、字符輸入流Reader
Reader是所有字符輸入流的父類,定義了所有字符輸入流都具有的共同特征。其內部提供的方法如下:
上圖中重載的三個read()方法都是用來讀數據的。
- int read():每次讀取一個字符,並把它轉換為 0~65535 的整數,然后返回讀取到的字符。讀到末尾返回-1。(為了提高 I/O 操作的效率,建議盡量使用下面兩種read()方法)
- int read(char[] cbuf):將讀取到的多個字符存儲到 cbuf 中,然后返回讀取的字符,讀到末尾時返回-1。
- int read(char[] cbuf,int off,int len):將讀取到的多個字符存儲進字符數組 cbuf,從索引 off 開始,長度為len字符,返回結果為讀取的字符數.
其中Reader的實現類FileReader是專門用於讀取文件中的數據,通過它將目標設備上的數據讀入到程序。
FileReader讀入文件舉例(讀取的hello.txt文件內容是:你好,我是中國人!):
package com.thr; import java.io.File; import java.io.FileReader; import java.io.IOException; /** * @author Administrator * @date 2020-02-23 * @desc 字符輸入流FileReader */ public class FileReaderTest { public static void main(String[] args) { //定義輸入流 FileReader fr =null; try { //1、創建文件對象 File file = new File("D:\\IO\\hello.txt"); //2、創建輸入流對象 fr = new FileReader(file); //循環讀取,讀取到末尾返回-1 int len ;//記錄每次讀取的字節個數 while((len = fr.read())!=-1){ System.out.print((char)len); } } catch (IOException e) { e.printStackTrace(); } finally { //釋放資源 try { fr.close(); } catch (IOException e) { e.printStackTrace(); } } } } 運行結果: 你好,我是中國人!
2、字符輸出流Writer
Writer是所有字符輸出流的父類,定義了所有字符輸出流都具有的共同特征。其內部提供的方法如下:
上圖中重載的五個write()方法都是用來寫數據的。
- void write(int c):把一個字符寫入到文件中。
- void write(char[] cbuf):把cbuf字符數組寫入到文件中。
- void write(char[] cbuf,int off,int len):把部分字符數組寫入到文件中,從 cbuf 數組的 off 索引開始,寫入len個字符。
- void write(String str):把一個字符串寫入到文件中。
- void write(String str,int off,int len):把部分字符串寫入到文件中,從 字符串的 off 索引開始,寫入len個字符。
其中Writer的實現類FileWriter是門用於讀出文件中的數據,通過它將數據從程序輸出到目標設備。
FileWriter輸出文件舉例:
package com.thr; import java.io.File; import java.io.FileWriter; import java.io.IOException; /** * @author Administrator * @date 2020-02-23 * @desc 字符輸出流FileWriter */ public class FileWriterTest { public static void main(String[] args) { //定義輸出流 FileWriter fw =null; try { //1、創建文件對象 File file = new File("D:\\IO\\a.txt"); //2、創建輸出流對象,保留原來的數據,在文件后面追加 fw = new FileWriter(file,true); //write():寫出一個字符(utf-8編碼) fw.write(20200223); fw.write("\r\n");//回車換行 //write(char[] buffer):把cbuf字符數組寫入到文件中。 fw.write("我是中國人".toCharArray()); fw.write("\r\n"); //write(char[] cbuf,int off,int len):把部分字符數組寫入到文件中,從 cbuf 數組的 off 索引開始,寫入len個字符。 fw.write("我是中國人".toCharArray(),1,4); fw.write("\r\n"); //write(String str):把一個字符串寫入到文件中。 fw.write("中國人"); fw.write("\r\n"); //write(String str,int off,int len):把部分字符串寫入到文件中,從 字符串的 off 索引開始,寫入len個字符。 fw.write("你好,我是中國人\n",3,6); } catch (IOException e) { e.printStackTrace(); } finally { //釋放資源 try { fw.close(); } catch (IOException e) { e.printStackTrace(); } } } }
注意:如果字符輸出流不調用close()方法關閉資源,數據只是保存到緩沖區,並不會保存到文件(這一點與FileOutputStream不同,即使它不調用close()方法依然會寫入到文件中)。
這里就得介紹一下flush()這個方法。
flush()和close()方法的區別:
flush():將流中的緩沖區緩沖的數據刷新到目的地中,刷新后,流還可以繼續使用。
close():關閉資源,但在關閉前會將緩沖區中的數據先刷新到目的地,否則丟失數據,然后在關閉流。流不可以使用。
而當我們親自去使用flush()方法的時候會產生一個疑問,就算我們在close()之前不加flush()方法也不會影響文件的寫出呀,那為什么還要用這個方法呢?先看一下下面的代碼吧:
package com.thr; import java.io.FileWriter; import java.io.IOException; /** * @author Administrator * @date 2020-02-24 * @desc flush方法的使用 */ public class WriterFlushTest { public static void main(String[] args) { //定義輸出流 FileWriter fw = null; //其實該步就是在明確數據要存放的目的地 try { fw=new FileWriter("D:\\flush.txt"); fw.write("介");//調用write方法,將字符串寫入到流中(使用write其實並沒有將字符串直接寫入到指定文件,而是存放在流中) fw.flush();//這一步才是將上一步write的所寫的字符串沖刷到指定的文件(在指定的文件打開后有字符串存在了) fw.write("紹"); fw.flush(); fw.write("flush方法"); fw.flush(); fw.close();//close()是一定要做的(其實close方法中調用了flush方法) fw.write("CCC");//close后若再write,會提示IO異常Stream closed } catch (IOException e) { e.printStackTrace(); } } }
小聲BB:我認為flush()方法可有可無,它就是將緩沖區的數據強制寫入到文件中,因為在調用完write()方法后數據並沒有寫入到文件中,而是保存在流中,此時我們可以調用flush()方法,也可以在最后用close()方法來將數據寫入文件,因為在close()的時候,也會進行一次flush的,因此close之前其實可以不用專門做flush的。但是在某些情況下,數據量較大的時候,在寫的過程中,可以進行階段性的flush。(話說我也不知道這樣做會有什么好處……)
3、字符流拷貝文件
package com.thr; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; /** * @author Administrator * @date 2020-02-23 * @desc 拷貝文本文件 */ public class FileReaderWriterTest { public static void main(String[] args) { //定義輸入流 FileReader fr =null; //定義輸出流 FileWriter fw =null; try { //創建讀入文件對象 File f1 = new File("D:\\IO\\hello.txt"); //創建讀出文件對象 File f2 = new File("C:\\a.txt"); fr = new FileReader(f1); fw = new FileWriter(f2); //循環讀取,讀取到末尾返回-1 char chars[] = new char[1024]; int len ;//記錄每次讀取的字節個數 while((len = fr.read(chars))!=-1){ fw.write(chars,0,len); } System.out.println("拷貝成功..."); } catch (IOException e) { e.printStackTrace(); } finally { //釋放資源 try { fr.close(); fw.close(); } catch (IOException e) { e.printStackTrace(); } } } }