Java IO流

2023-07-14 06:01:46

IO流的基本介紹:

IO流的概述:
i 表示intput,是資料從硬碟檔案讀入到記憶體的過程,稱之輸入,負責讀。
o 表示output,是記憶體程式的資料從記憶體到寫出到硬碟檔案的過程,稱之輸出,負責寫。




IO流的分類:

按方向分類:

  • 輸入流
  • 輸出流

按流中的資料最小單位分為:按流中的資料最小單位分為:

  • 位元組流: 可以操作所有型別的檔案(包括音視屏圖片等)
  • 字元流: 只能操作純文字的檔案(包括java檔案, txt檔案等)

總結流的四大類:總結流的四大類:

  • 位元組輸入流:以記憶體為基準,來自磁碟檔案/網路中的資料以位元組的形式讀入到記憶體中去的流稱為位元組輸入流。

  • 位元組輸出流:以記憶體為基準,把記憶體中的資料以位元組寫出到磁碟檔案或者網路中去的流稱為位元組輸出流。

  • 字元輸入流:以記憶體為基準,來自磁碟檔案/網路中的資料以字元的形式讀入到記憶體中去的流稱為字元輸入流。

  • 字元輸出流:以記憶體為基準,把記憶體中的資料以字元寫出到磁碟檔案或者網路媒介中去的流稱為字元輸出




位元組流的使用位元組流的使用




步驟:

1、檔案位元組輸入流
2、建立位元組輸入流
3、檔案位元組輸入流: 實現類FileInputStream


作用:以記憶體為基準,把磁碟檔案中的資料以位元組的形式讀取到記憶體中去。


構造器如下:

構造器 說明
public FileInputStream(File file) 建立位元組輸入流管道與原始檔物件接通
public FileInputStream(String pathname) 建立位元組輸入流管道與原始檔路徑接通


範例程式碼:

"""

public static void main(String[] args) throws FileNotFoundException {
// 寫法一: 建立位元組輸入流與原始檔物件接通
InputStream inp = new FileInputStream(new File("/file-io-app/src/test.txt"));

}

"""

"""

public static void main(String[] args) throws FileNotFoundException {
// 寫法二: 建立位元組輸入流管道與原始檔路徑接通
InputStream inp = new FileInputStream("/file-io-app/src/test.txt");

}

"""

每次讀取一個位元組

方法名稱 說明
read() 每次讀取一個位元組返回,如果位元組已經沒有可讀的返回-1

例如我們讀取的記事本檔案中內容是: abcd123



"""

public static void main(String[] args) throws Exception {
InputStream inp = new FileInputStream("/Users/chenyq/Documents/learn_Java/code/file-io-app/src/test.txt");
int a = inp.read();
System.out.println(a); // 97
System.out.println((char) a); // a
// 一次輸入一個位元組
System.out.println(inp.read()); // 98
System.out.println(inp.read()); // 99
System.out.println(inp.read()); // 100
System.out.println(inp.read()); // 49
System.out.println(inp.read()); // 50
System.out.println(inp.read()); // 51
// 無位元組可讀返回-1
System.out.println(inp.read()); // -1

}

"""


我們可以通過迴圈遍歷出檔案中的位元組


"""

public static void main(String[] args) throws Exception {
InputStream inp = new FileInputStream("/Users/chenyq/Documents/learn_Java/code/file-io-app/src/test.txt");
int b;
while ((b = inp.read()) != -1) {
    System.out.print((char) b); // abcd123
}

}

"""


每次讀取一個位元組存在以下問題

  • 效能較慢

  • 讀取中文字元輸出無法避免亂碼問題。


每次讀取一個陣列

方法名稱 說明
read(byte[] buffer) 每次讀取一個位元組陣列, 返回讀取了幾個位元組,如果位元組已經沒有可讀的返回-1

定義一個位元組陣列, 用於接收讀取的位元組數

例如下面程式碼中, 檔案中的內容是: abcd123, 每次讀取三個位元組, 每一次讀取都會覆蓋上一次陣列中的內容, 但是第三次讀取唯讀取了一個字元, 所以只覆蓋了上一次讀取的字元陣列的第一個元素, 結果是: 312



"""

public static void main(String[] args) throws Exception {
InputStream inp = new FileInputStream("/Users/chenyq/Documents/learn_Java/code/file-io-app/src/test.txt");
// 定義一個長度為3的位元組陣列
byte[] arr = new byte[3];

// 第一次讀取一個位元組陣列
int len1 = inp.read(arr);
System.out.println("讀取位元組數: " + len1); // 讀取位元組數: 3
// 對位元組陣列進行解碼
String res1 = new String(arr);
System.out.println(res1); // abc

// 第二次讀取一個位元組陣列
int len2 = inp.read(arr);
System.out.println("讀取位元組數: " + len2); // 讀取位元組數: 3
// 對位元組陣列進行解碼
String res2 = new String(arr);
System.out.println(res2); // d12

// 第三次讀取一個位元組陣列
int len3 = inp.read(arr);
System.out.println("讀取位元組數: " + len3); // 讀取位元組數: 1
// 對位元組陣列進行解碼
String res3 = new String(arr);
System.out.println(res3); // 312

// 無位元組可讀返回-1
System.out.println(inp.read()); // -1

}

"""


1、String第二個引數可以指定開始位置, 第三個引數可以指定結束位置, 可以用這兩個引數解決第三次讀取的弊端

2、並且迴圈改進優化程式碼



"""

public static void main(String[] args) throws Exception {
InputStream inp = new FileInputStream("/Users/chenyq/Documents/learn_Java/code/file-io-app/src/test.txt");

byte[] arr = new byte[3];
int len;
while ((len = inp.read(arr)) != -1) {
    String res = new String(arr, 0, len);
    System.out.print(res); // abcd123
}

}

"""


每次讀取一個陣列存在的弊端:

1、讀取的效能得到了提升

2、讀取中文字元輸出無法避免亂碼問題。


一次讀取全部位元組

為解決中文亂碼問題我們可以定義一個與檔案一樣大的位元組陣列,一次性讀取完檔案的全部位元組。

弊端: 如果檔案過大,位元組陣列可能引起記憶體溢位。


解決方案一:

自己定義一個位元組陣列與檔案的大小一樣大,然後使用讀取位元組陣列的方法,一次性讀取完成。


"""

public static void main(String[] args) throws Exception {
File file = new File("/Users/chenyq/Documents/learn_Java/code/file-io-app/src/test.txt");
InputStream inp = new FileInputStream(file);

// 建立一個與檔案大小一樣的位元組陣列
byte[] arr = new byte[(int) file.length()];

// 讀取檔案, 獲取讀取的位元組長度
int len = inp.read(arr);
System.out.println(len); // 252

// 對位元組陣列進行解碼
String res = new String(arr);
System.out.println(res);
// abcd123我愛Java學習Java.abcd123我愛Java學習Java.abcd123我愛Java學習Java.
// abcd123我愛Java學習Java.abcd123我愛Java學習Java.abcd123我愛Java學習Java.
// abcd123我愛Java學習Java.abcd123我愛Java學習Java.abcd123我愛Java學習Java.

}

"""


方式二:

官方為位元組輸入流InputStream提供瞭如下API可以直接把檔案的全部資料讀取到一個位元組陣列中



方法名稱 說明
readAllBytes() 直接讀取當前位元組輸入流對應的檔案物件的全部位元組資料, 然後裝到一個位元組陣列返回


"""

public static void main(String[] args) throws Exception {
InputStream inp = new FileInputStream("/Users/chenyq/Documents/learn_Java/code/file-io-app/src/test.txt");

// 獲取檔案的全部位元組, 並返回一個位元組陣列
byte[] arr = inp.readAllBytes();
// 對位元組陣列進行解碼
String res = new String(arr);
System.out.println(res);
// abcd123我愛Java學習Java.abcd123我愛Java學習Java.abcd123我愛Java學習Java.
// abcd123我愛Java學習Java.abcd123我愛Java學習Java.abcd123我愛Java學習Java.
// abcd123我愛Java學習Java.abcd123我愛Java學習Java.abcd123我愛Java學習Java.

}

"""


檔案位元組輸出流

建立位元組輸出流
檔案位元組輸出流: 實現類FileOutputStream

作用:以記憶體為基準,把記憶體中的資料以位元組的形式寫出到磁碟檔案中去的流。



構造器如下:

構造器 說明
FileOutputStream(File file) 建立位元組輸出流管道與原始檔物件接通
FileOutputStream(String filepath) 建立位元組輸出流管道與原始檔路徑接通

"""

public static void main(String[] args) throws Exception {
// 寫法一: 建立輸出流與原始檔物件接通
OutputStream oup = new FileOutputStream(new File("/Users/chenyq/Documents/learn_Java/code/file-io-app/src/test.txt"));

}

"""



"""

public static void main(String[] args) throws Exception {
// 寫法二: 建立輸出與原始檔路徑接通(常用)
OutputStream oup = new FileOutputStream("/Users/chenyq/Documents/learn_Java/code/file-io-app/src/test.txt");

}

"""


寫入檔案輸出流
檔案位元組輸出流寫資料出去的API:

方法名稱 說明
write(int a) 寫一個位元組出去
write(byte[] buffer) 寫一個位元組陣列出去
write(byte[] buffer , int pos , int len) 寫一個位元組陣列的一部分出去

流的重新整理與關閉API:

方法 說明
flush() 重新整理流,還可以繼續寫資料
close() 關閉流,釋放資源,但是在關閉之前會先重新整理流。一旦關閉,就不能再寫資料

注意: 寫入資料必須重新整理資料, 流使用完成後需要關閉


寫一個位元組出去



"""

public static void main(String[] args) throws Exception {
OutputStream oup = new FileOutputStream("/Users/chenyq/Documents/learn_Java/code/file-io-app/src/test.txt");

oup.write('a');
// 支援寫入編碼
oup.write(97);
// 漢字佔三個位元組, 所以該方法不可以寫入漢字
// oup.write('我');

// 寫資料必須重新整理資料
oup.flush();
// 重新整理流後可以繼續寫入資料
oup.write('b');
// 使用完後需要關閉流, 關閉後不能再寫入資料
oup.close();

}

"""


寫一個位元組陣列出去


"""

public static void main(String[] args) throws Exception {
OutputStream oup = new FileOutputStream("/Users/chenyq/Documents/learn_Java/code/file-io-app/src/test.txt");

// 定義一個位元組陣列
byte[] arr = {'a', 98, 'b', 'c'};
// 寫入中文, 需要將中文編碼成位元組陣列
byte[] chinese = "中國".getBytes();

// 寫入英文位元組陣列
oup.write(arr);
// 寫入中文位元組陣列
oup.write(chinese);

// 關閉流(關閉之前會重新整理)
oup.close();

}

"""


寫入一個位元組陣列的一部分


"""

public static void main(String[] args) throws Exception {
OutputStream oup = new FileOutputStream("/Users/chenyq/Documents/learn_Java/code/file-io-app/src/test.txt");

// 定義一個位元組陣列
byte[] arr = {'a', 98, 'b', 'c'};
// 寫入陣列的第二個和第三個元素
oup.write(arr, 1, 2);

// 關閉流(關閉之前會重新整理)
oup.close();

}

"""


補充知識:

補充一: 寫入內容時, 如果需要換行可將\r\n(window支援輸入\n但是有些系統不支援, 為了具備通用性使用\r\n)轉為位元組陣列寫入, 實現換行效果

"""

public static void main(String[] args) throws Exception {
OutputStream oup = new FileOutputStream("/Users/chenyq/Documents/learn_Java/code/file-io-app/src/test.txt");

// 定義一個位元組陣列
byte[] arr = {'a', 98, 'b', 'c'};
oup.write(arr);
// 寫入換行
oup.write("\r\n".getBytes());
// 寫入陣列的第二個和第三個元素
oup.write(arr, 1, 2);

// 關閉流(關閉之前會重新整理)
oup.close();

}

"""


補充二: 當寫入檔案時, 會先將原來檔案清空, 再寫入新的資料, 如果我們想在原來檔案資料的基礎上追加新的資料, 這時候就需要將構造器的第二個引數設定為true


構造器 說明
FileOutputStream(File file,boolean append) 建立位元組輸出流管道與原始檔物件接通,可追加資料
FileOutputStream(String filepath,boolean append) 建立位元組輸出流管道與原始檔路徑接通,可追加資料

"""

public static void main(String[] args) throws Exception {
  // 設定為true即可
OutputStream oup = new FileOutputStream("/Users/chenyq/Documents/learn_Java/code/file-io-app/src/test.txt", true);

}

"""


檔案拷貝練習檔案拷貝練習

需求:

把test.pdf檔案複製到其他目錄下的newtest.pdf檔案中

思路分析:

根據資料來源建立位元組輸入流物件

根據目的地建立位元組輸出流物件

讀寫資料,複製視訊

釋放資源



範例程式碼:

"""

public static void main(String[] args) {
try {
    // 建立要複製檔案的位元組輸入流
    InputStream inp = new FileInputStream("/Users/chenyq/Documents/learn_Java/code/file-io-app/src/test.pdf");
    // 建立目標路徑的位元組輸出流
    OutputStream oup = new FileOutputStream("/Users/chenyq/Documents/newtest.pdf");

    // 使用檔案輸入流獲取要複製檔案的全部資料的位元組陣列
    byte[] arr = inp.readAllBytes();
    // 使用檔案輸出流將位元組陣列寫入目標檔案
    oup.write(arr);
    System.out.println("複製成功!");

    // 釋放資源
    inp.close();
    oup.close();
} catch (IOException e) {
    e.printStackTrace();
}

}

"""


疑問: 位元組流可以拷貝什麼型別的檔案?

任何檔案的底層都是位元組,拷貝是一字不漏的轉移位元組,只要前後檔案格式、編碼一致沒有任何問題。
總結: 位元組流適合拷貝檔案, 但是不適合進行中文的輸出輸出