1.操作資料單位:字節流、字符流
2.資料的流向:輸入流、輸出流
3.流的角色:節點流、處理流
@Test
public void fileReaderTest() {
FileReader fr = null;
try {
File file = new File("hello.txt");
// System.out.println(file.getAbsolutePath());
// 準備具體的文件字符流
fr = new FileReader(file);
// 每次調用read()即讀取一個字符
// 當方法回傳-1時,代表以至文件末尾
int aChar;
while ((aChar = fr.read()) != -1) {
System.out.print((char) aChar);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// 關閉流
if (fr != null) {
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
有快遞車一次載5個商品,直到載完全部的商品;每次會回報這次實際載了幾個商品
@Test
public void fileReaderWithCharArr() {
FileReader fr = null;
try {
File file = new File("hello.txt");
fr = new FileReader(file);
// 新建一個字符陣列,可以至多一次讀5個字符
char[] chBuffer = new char[5];
// len 接收每次讀取的實際字符數
// 若已至文件末尾,回傳-1
int len;
while ((len = fr.read(chBuffer)) != -1) {
// 方式一
// for (int i = 0; i < len; i++) {
// System.out.print(chBuffer[i]);
// }
// 方式二
// 將char陣列轉字串輸出
String s = new String(chBuffer, 0, len);
System.out.print(s);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fr != null) {
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@Test
public void fileWriterTest() {
FileWriter fw = null;
try {
File file = new File("hello1.txt");
// new FileWriter(File file, boolean append)
// append 屬性默認為false,新寫出的內容會覆蓋原來的內容
// append 屬性為true,則會在原有基礎上附加內容
fw = new FileWriter(file, false);
// 方式一
// fw.write("I am using file writer at the beginning.\n");
// fw.write("this is another line");
// 方式二
String content = "I am using file writer at the beginning.\nthis is another line";
fw.write(content.toCharArray());
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fw != null) {
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@Test
public void copyDocumentTest() {
FileReader fr = null;
FileWriter fw = null;
try {
File srcDoc = new File("hello.txt");
File destDoc = new File("hello2.txt");
fr = new FileReader(srcDoc);
fw = new FileWriter(destDoc, false);
char[] chBuffer = new char[5];
int len;
while ((len = fr.read(chBuffer)) != -1) {
// len的長度為實際讀取的字符數
// 因此也可以作為寫出的實際字符數
fw.write(chBuffer, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fw != null) {
fw.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (fr != null) {
fr.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 圖片複製
*/
@Test
public void imageCopyTest() {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
File srcImg = new File("girl.png");
File destImg = new File("girl2.png");
// 準備字節輸入流物件
fis = new FileInputStream(srcImg);
// 準備字節輸出流物件
fos = new FileOutputStream(destImg);
// 一次讀取至多1024 bytes
byte[] byteBuffer = new byte[1024];
// 每次讀取會回傳實際讀取的bytes數
// 若回傳-1,代表已讀完整的圖片
int len;
while ((len = fis.read(byteBuffer)) != -1) {
// 依據本次實際讀取的字節數寫出
fos.write(byteBuffer,0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
提高流的讀取、寫入的速度,原因:內部提供了一個緩衝區
@Test
public void videoCopyTest() {
String srcPath = "C:\\Users\\ching\\Desktop\\google-video.mp4";
String destPath = "C:\\Users\\ching\\Desktop\\google-video1.mp4";
long start = System.currentTimeMillis();
copyFile(srcPath,destPath);
long end = System.currentTimeMillis();
System.out.println(end - start); // 4599 1321
}
private void copyFile(String srcPath, String destPath) {
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
File srcImg = new File(srcPath);
File destImg = new File(destPath);
// 準備字節輸入流物件
FileInputStream fis = new FileInputStream(srcImg);
bis = new BufferedInputStream(fis);
// 準備字節輸出流物件
FileOutputStream fos = new FileOutputStream(destImg);
bos = new BufferedOutputStream(fos);
// 一次讀取至多1024 bytes
byte[] byteBuffer = new byte[1024];
// 每次讀取會回傳實際讀取的bytes數
// 若回傳-1,代表已讀完整的圖片
int len;
while ((len = bis.read(byteBuffer)) != -1) {
// 依據本次實際讀取的字節數寫出
bos.write(byteBuffer, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (bis != null) {
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bos != null) {
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@Test
public void copyDocumentTest() {
BufferedReader br = null;
BufferedWriter bw = null;
try {
File srcDoc = new File("hello.txt");
File destDoc = new File("hello2.txt");
FileReader fr = new FileReader(srcDoc);
FileWriter fw = new FileWriter(destDoc, false);
br = new BufferedReader(fr);
bw = new BufferedWriter(fw);
char[] chBuffer = new char[5];
int len;
// 方式一
// while ((len = br.read(chBuffer)) != -1) {
// // len的長度為實際讀取的字符數
// // 因此也可以作為寫出的實際字符數
// bw.write(chBuffer, 0, len);
// }
// 方式二
// 我們可以使用BufferedReader.readLine() 代表一次讀取一行
// 但readLine() 不會配給我們換行符
// 所以可以另外調BufferedReader.newLine()換行
// 當readLine()回傳 null 代表文件已經讀到末尾了
String str;
while ((str = br.readLine()) != null) {
bw.write(str);
// 換行
bw.newLine();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// 只要關閉最外層的流,內層的就會一起關閉了
try {
if (bw != null) {
bw.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (br != null) {
br.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
作用:提供 字符流(char) 與 字節流(byte) 之間的轉換
類型:
InputStreamReader 例子
/**
* 例外要以try-catch-finally處理較正規
* @throws IOException
*/
@Test
public void inputStreamReaderTest() throws IOException {
// 使用文件字節流讀取到console
// 可能會有中文字亂碼的危機
FileInputStream fis = new FileInputStream("words.txt");
// 因此要想辦法將字節流 轉換成 字符流 (解碼的過程需提供文件儲存時 指定的編碼 )
InputStreamReader isr = new InputStreamReader(fis, "UTF-8");
char[] chArr = new char[20];
int len;
while ((len = isr.read(chArr)) != -1) {
// 輸出方式一
String str = new String(chArr, 0, len);
// 在console輸出
System.out.print(str);
// 輸出方式二
// for (int i = 0; i < len; i++) {
// System.out.print(chArr[i]);
// }
}
// 關閉最外層的inputStreamReader就可以了
isr.close();
}
/**
* 例外要以try-catch-finally處理較正規
* @throws IOException
*/
@Test
public void docUTF8ToBIG5() throws IOException {
File srcFile = new File("words.txt");
File destFile = new File("words_BIG5.txt");
FileInputStream fis = new FileInputStream(srcFile);
FileOutputStream fos = new FileOutputStream(destFile);
// byte[] => char[] 解碼
InputStreamReader isr = new InputStreamReader(fis, "UTF-8");
// char[] => byte[] 編碼
OutputStreamWriter osr = new OutputStreamWriter(fos, "BIG5");
char[] chBuffer = new char[20];
int len;
while((len = isr.read(chBuffer)) != -1) {
osr.write(chBuffer, 0, len);
}
osr.close();
isr.close();
}
使用 ByteArrayOutputStream 的好處:若輸入流讀取後的資料直接寫入 ByteArrayOutputStream 儲存,就不用擔心每個字符到底對應幾個byte,因為在使用byte[] 盛裝資料時,可能會因為本次array空間不足就只儲存一個字符對應的部分bytes、而導致當要將bytes轉回字符時而產生亂碼的問題;ByteArrayOutputStream 底層是使用陣列append所有bytes之後,再一次性地將資料轉換成字串
/**
* 使用 ByteArrayOutputStream 實現複製文件
* 例外要以try-catch-finally處理較正規
* @throws IOException
*/
@Test
public void byteArrayOutputStreamTest() throws IOException {
FileInputStream fis = new FileInputStream("hello.txt");
FileOutputStream fos = new FileOutputStream("hello2.txt");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[20];
int len;
while ((len = fis.read(buffer)) != -1) {
// 先將從byte[] 讀取的字節保存到 ByteArrayOutputStream
baos.write(buffer, 0, len);
}
// 再一次性地將 ByteArrayOutputStream 的資料寫入到文件
baos.writeTo(fos);
// 關閉流
baos.close();
fos.close();
fis.close();
}
/**
* 使用 StringBuilder 接收讀取的字符資料並一次性輸出至console
* 例外要以try-catch-finally處理較正規
*/
@Test
public void consoleStringBuilderTest() throws IOException {
FileInputStream fis = new FileInputStream("hello.txt");
InputStreamReader isr = new InputStreamReader(fis, "Big5");
// 輸出console 準備 StringBuilder 接收資料
StringBuilder sb = new StringBuilder();
char[] chBuffer = new char[20];
int len;
while ((len = isr.read(chBuffer)) != -1) {
sb.append(new String(chBuffer, 0, len));
}
System.out.println(sb.toString());
isr.close();
}