文章目錄
零、本講學習目標
1、掌握字節流緩衝區的使用
2、掌握字節緩衝流的使用
3、掌握字符流操作文件的使用
4、掌握字符轉換流的使用
一、字節流的緩衝區
1、爲何引入字節流的緩衝區
上一講,我們利用文件字節流實現了文件拷貝,但是一個字節一個字節地讀寫,效率是十分低下的,這就好比從北京運送快遞到上海,如果有一萬件快遞,一件一件的運送就必須運輸一萬次,這樣的效率顯然非常低。爲了減少運輸次數,可以先把一批快遞裝在一個車廂中,這樣就可以成批地運送快遞,這時的車廂就相當於一個臨時緩衝區。同理,在文件拷貝過程中,通過以字節形式逐個拷貝,效率也非常低。爲此,可以定義一個字節數組緩衝區,在拷貝文件時,就可以一次性讀取多個字節的數據。
2、案例演示:採用文件字節流與字節流緩衝區實現文件拷貝
- 將源文件source.png拷貝到net.hw.lesson25包裏
- 創建Example2501
package net.hw.lesson25;
import java.io.FileInputStream;
import java.io.FileOutputStream;
/**
* 功能:拷貝文件
* 採用文件字節流和字節流緩衝區
* 作者:華衛
* 日期:2020年05月26日
*/
public class Example2501 {
public static void main(String[] args) throws Exception {
// 創建文件字節輸入流
FileInputStream fis = new FileInputStream("src/net/hw/lesson25/source.png");
// 創建文件字節輸出流
FileOutputStream fos = new FileOutputStream("src/net/hw/lesson25/target.png");
// 獲取開始拷貝文件的系統時間
long startTime = System.currentTimeMillis();
// 定義字節數組作爲緩衝區
byte[] buffer = new byte[1024];
// 讀取源文件,寫入目標文件
int len = 0;
while ((len = fis.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
// 獲取結束拷貝文件的系統時間
long endTime = System.currentTimeMillis();
// 輸出拷貝文件耗費時間
System.out.println("拷貝文件耗費時間:" + (endTime - startTime) + "毫秒");
}
}
- 運行程序,查看結果
- 查看拷貝生成的目標文件target.png
- 對比上一講案例Example2408,逐個字節拷貝文件的耗時
- 說明:使用緩衝區讀寫文件可以有效地提高程序的傳輸效率。程序中的緩衝區就是一塊內存,該內存主要用於存放暫時輸入輸出的數據,由於使用了緩衝區減少了對文件的操作次數,因此可以顯著地提高讀寫數據的效率。
二、字節緩衝流
1、字節緩衝流概述
除了定義字節緩衝區來提高文件拷貝效率外,IO中還提供了兩個字節緩衝流來提高文件拷貝效率:BufferedInputStream和BufferedOutputStream。它們的構造方法中分別接收InputStream和OutputStream類型的對象作爲參數,在讀寫數據時提供緩衝功能。
2、案例演示:採用字節緩衝流但不採用字節流緩衝區拷貝文件
- 創建Example2502
package net.hw.lesson25;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
/**
* 功能:拷貝文件
* 採用字節緩衝流,不採用字節流緩衝區
* 作者:華衛
* 日期:2020年05月26日
*/
public class Example2502 {
public static void main(String[] args) throws Exception {
// 創建緩衝字節輸入流
BufferedInputStream bis = new BufferedInputStream(
new FileInputStream("src/net/hw/lesson25/source.png"));
// 創建緩衝字節輸出流
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream("src/net/hw/lesson25/target.png"));
// 獲取開始拷貝文件的系統時間
long startTime = System.currentTimeMillis();
// 讀取源文件,寫入目標文件
int len = 0;
while ((len = bis.read()) != -1) {
bos.write(len);
}
// 獲取結束拷貝文件的系統時間
long endTime = System.currentTimeMillis();
// 輸出拷貝文件耗費時間
System.out.println("拷貝文件耗費時間:" + (endTime - startTime) + "毫秒");
}
}
- 運行程序,查看結果
- 查看拷貝生成的目標文件target.png
- 說明:緩衝流對象bis和bos內部都定義了一個大小爲8192的字節數組,當調用read()或write()方法讀寫數據時,首先將讀寫的數據存入到定義好的字節數組,然後將字節數組的數據一次性地讀寫到文件中,這種方式與字節流的緩衝區類似,都對數據進行了緩衝,從而有效地提高了數據的讀寫效率。
- 其實還可以將緩衝字節流和字節流緩衝區兩種方式結合起來,更好地提高文件讀寫效率。大家可以參看《Java案例:幾種方式拷貝文件的耗時比較》。
三、字符流
(一)字符流概述
除了字節流,JDK還提供了用於實現字符操作的字符流,同字節流一樣,字符流也有兩個抽象的頂級父類,分別是Reader和Writer。
1、字符輸入流Reader
2、字符輸出流Writer
(二)利用字符流讀文件
1、利用文件字符流逐個字符讀文件
- 在當前包裏創建文本文件in.txt
- 創建Example2503
package net.hw.lesson25;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
/**
* 功能:利用文件字符流逐個字符讀文件
* 作者:華衛
* 日期:2020年05月26日
*/
public class Example2503 {
public static void main(String[] args) {
// 聲明文件字符輸入流
FileReader fr = null;
try {
// 創建文件字符輸入流
fr = new FileReader("src/net/hw/lesson25/in.txt");
// 循環讀取文件字符輸入流的數據
int len = 0;
while ((len = fr.read()) != -1) {
System.out.print((char) len);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 判斷文件字符輸入流是否爲空
if (fr != null) {
try {
// 關閉文件字符輸入流
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
- 運行程序,查看結果
- 說明:有興趣的同學,可以添加代碼輸出文件拷貝耗費的時間。
2、利用文件字符流與字符流緩衝區讀取文件
- 創建Example2504
package net.hw.lesson25;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
/**
* 功能:利用文件字符流與字符流緩衝區讀取文件
* 作者:華衛
* 日期:2020年05月26日
*/
public class Example2504 {
public static void main(String[] args) {
// 聲明文件字符輸入流
FileReader fr = null;
try {
// 創建文件字符輸入流
fr = new FileReader("src/net/hw/lesson25/in.txt");
// 定義字符數組作爲緩衝區
char[] buffer = new char[1024];
// 循環讀取文件字符輸入流的數據
int len = 0;
while ((len = fr.read(buffer)) != -1) {
System.out.print(new String(buffer, 0, len));
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 判斷文件字符輸入流是否爲空
if (fr != null) {
try {
// 關閉文件字符輸入流
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
- 說明:在循環讀取文件字符輸入流中,不能直接打印字符緩衝區,要利用
new String(buffer, 0, len)
將字符數組充當的緩衝區轉換成字符串來輸出。 - 運行程序,查看結果
3、利用字符緩衝流逐個字符讀取文件
- 創建Example2505
package net.hw.lesson25;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
/**
* 功能:利用字符緩衝流逐個字符讀文件
* 作者:華衛
* 日期:2020年05月26日
*/
public class Example2505 {
public static void main(String[] args) {
// 聲明字符緩衝入流
BufferedReader br = null;
try {
// 創建字符緩衝輸入流
br = new BufferedReader(new FileReader("src/net/hw/lesson25/in.txt"));
// 循環讀取字符緩衝輸入流的數據
int len = 0;
while ((len = br.read()) != -1) {
System.out.print((char) len);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 判斷字符緩衝輸入流是否爲空
if (br != null) {
try {
// 關閉字符緩衝輸入流
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
- 運行程序,查看結果
4、課堂練習:利用緩衝字符流與字符流緩衝區讀取文件
(三)利用字符流寫文件
1、利用字符流非追加模式寫文件
非追加模式寫文件,如果文件不存在,就創建新文件再寫入內容;如果文件存在,先清空文件內容,然後在寫入新內容。
- 創建Example2507
package net.hw.lesson25;
import java.io.FileWriter;
import java.io.IOException;
/**
* 功能:利用字符流非追加模式寫文件
* 作者:華衛
* 日期:2020年05月26日
*/
public class Example2507 {
public static void main(String[] args) {
FileWriter fw = null;
try {
// 創建文件字符輸出流
fw = new FileWriter("src/net/hw/lesson25/out.txt");
// 將字符串寫入指定文件
fw.write("十年磨劍意自閒,\n");
fw.write("一朝出鞘氣沖天。\n");
fw.write("男兒當有鴻鵠志,\n");
fw.write("雄心萬丈跨羣山。\n");
// 提示用戶數據寫入文件成功
System.out.println("數據成功寫入文件!");
} catch (IOException e) {
e.printStackTrace();
} finally {
// 判斷文件字符流是否爲空
if (fw != null) {
try {
// 關閉文件字符輸出流
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
- 運行程序,查看結果
- 查看生成的輸出文件out.txt
- 說明:由於採用的是非追加模式寫文件,那麼再執行程序,你會發現輸出文件out.txt的內容依然如故,只有四行詩。
2、利用字符流追加模式寫文件
追加模式寫文件,如果文件不存在,就創建新文件再寫入內容;如果文件存在,在文件末尾追加新內容。
- 創建Example2508
package net.hw.lesson25;
import java.io.FileWriter;
import java.io.IOException;
/**
* 功能:利用字符流追加模式寫文件
* 作者:華衛
* 日期:2020年05月26日
*/
public class Example2508 {
public static void main(String[] args) {
FileWriter fw = null;
try {
// 創建文件字符輸出流,採用追加模式寫文件
fw = new FileWriter("src/net/hw/lesson25/out.txt", true);
// 將字符串寫入指定文件
fw.write("滾滾紅塵意難安,\n");
fw.write("欲作蠟炬兩頭燃。\n");
fw.write("不羨平淡八十歲,\n");
fw.write("但求絢爛一瞬間。\n");
// 提示用戶數據寫入文件成功
System.out.println("數據成功寫入文件!");
} catch (IOException e) {
e.printStackTrace();
} finally {
// 判斷文件字符流是否爲空
if (fw != null) {
try {
// 關閉文件字符輸出流
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
- 運行程序,查看結果
- 查看輸出文件out.txt
- 再次運行程序,你會發現輸出文件out.txt又添加四行。
(四)利用字符流拷貝文件
1、文件拷貝思路
利用字符流拷貝文件,跟利用字節流拷貝文件思路完全類似,也有四種方式拷貝文件。是否使用字符緩衝流,是否使用字符流緩衝區,一搭配,共有四種處理方式。下面我只給出第四種方式:利用字符緩衝流與字符流緩衝區拷貝文件。
2、案例演示:利用字符緩衝流與字符流緩衝區拷貝文件
比如將當前包裏的in.txt文件拷貝到當前包裏的result.txt。
- 創建Example2509
package net.hw.lesson25;
import java.io.*;
/**
* 功能:利用字符緩衝流與字符流緩衝區拷貝文件
* 作者:華衛
* 日期:2020年05月26日
*/
public class Example2509 {
public static void main(String[] args) {
// 聲明字符緩衝入流
BufferedReader br = null;
// 聲明字符緩衝輸出流
BufferedWriter bw = null;
try {
// 創建字符緩衝輸入流
br = new BufferedReader(new FileReader("src/net/hw/lesson25/in.txt"));
// 創建字符緩衝輸出流
bw = new BufferedWriter(new FileWriter("src/net/hw/lesson25/result.txt"));
// 定義字符數組作爲緩衝區
char[] buffer = new char[1024];
// 循環讀取字符緩衝輸入流的數據
int len = 0;
while ((len = br.read(buffer)) != -1) {
bw.write(buffer, 0, len);
}
// 提示用戶文件拷貝成功
System.out.println("恭喜,文件拷貝成功!");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 判斷字符緩衝輸入流是否爲空
if (br != null) {
try {
// 關閉字符緩衝輸入流
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
// 判斷字符緩衝輸出流是否爲空
if (bw != null) {
try {
// 關閉字符緩衝輸出流
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
- 運行程序,查看結果
- 查看拷貝生成的目標文件result.txt
四、轉換流
1、轉換流概述
- 在JDK中,提供了兩個類用於實現將字節流轉換爲字符流,它們分別是nputStreamReader和OutputStreamWriter。
- InputStreamReader是Reader的子類,它可以將一個字節輸入流轉換成字符輸入流,方便直接讀取字符。
- OutputStreamWriter是Writer的子類,它可以將一個字節輸出流轉換成字符輸出流,方便直接寫入字符。
- 將字節流轉換成字符流之後,往往再轉換成字符緩衝流,利用其提供的行讀取方法readLine()可以更好地提高讀寫效率。
可以參看《Java講課筆記24:字節流》裏的案例Example2403:行讀取鍵盤輸入的數據。
2、案例演示:利用轉換流和緩衝流實現文件拷貝
- 創建Example2510
package net.hw.lesson25;
import java.io.*;
/**
* 功能:利用轉換流和緩衝流實現文件拷貝
* 作者:華衛
* 日期:2020年05月26日
*/
public class Example2510 {
public static void main(String[] args) throws Exception {
// 創建字符緩衝輸入流
BufferedReader br = new BufferedReader(new InputStreamReader(
new FileInputStream("src/net/hw/lesson25/in.txt")));
// 創建字符緩衝輸出流
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
new FileOutputStream("src/net/hw/lesson25/target.txt")));
// 定義文本行變量
String nextLine = null;
// 採用循環,行讀取文件
while ((nextLine = br.readLine()) != null) {
System.out.println(nextLine); // 輸出到控制檯
bw.write(nextLine); // 寫入目標文件
bw.newLine(); // 換行
}
// 關閉字符緩衝流
br.close();
bw.close();
}
}
- 運行程序,查看結果
- 查看拷貝生成的目標文件target.txt
- 注意:文件拷貝完成之後,必須關閉字符緩衝流。如果不關閉字符緩衝輸出流,那麼只在控制檯輸出了文件內容,但是目標文件target.txt卻是空的,數據還在緩衝流裏,當然可以使用flush()方法將數據刷新到文件裏,但是最好養成用完流對象之後關閉的好習慣。
五、課後作業
任務1、將兩個文本文件合併成一個新的文本文件
- 創建文本文件in01.txt,隨意輸入一些內容
- 創建文本文件in02.txt,隨意輸入一些內容
- 利用文件字符流和字符緩衝流將in01.txt和in02.txt內容讀取出來寫入out.txt文件
任務2、將某個目錄下的全部文本文件合併生成一個文本文件
說明:完成這個任務,要用到下一講的File類,利用File類提供的方法可以獲取某個目錄下的全部文本文件名列表,然後遍歷讀取所有文件,將其全部寫入一個目標文件。