Java 文件 IO 操作

  • 2020 年 10 月 7 日
  • 筆記

window 路徑分割符:
  \ 表示 windows 系統文件目錄分割符
  java 程式碼在 windows 下寫某個文件的話需要下面的方式

  D:\\soft\\sdclass.txt  其中一個單斜杠是用來轉義的

  程式碼和文件目錄的關係: 對文件和目錄增刪改查
  IO,輸入和輸出 Input/Output
  把持久化設備上的數據讀取到記憶體中的動作稱為輸入, Input 操作
  記憶體中的數據持久化到設備上的動作, Output 輸出操作
  一般把輸入和輸出動作稱為 IO 操作, IO 分為網路 IO 和文件 IO

 java 文件類 File :  

  主要是對電腦文件目錄的操作,對文件和目錄的增刪改查, File 類表示磁碟中存在的文件和目錄
  實現了 Serializable, Comparable 兩大介面,可進行序列化和比較
  File.separator 目錄分隔符,在不同的系統下不一樣, windows 下是 “\\” , mac/Linux 下是 “/”,操作文件時可以用來連接目錄的分隔符

  常見的構造函數:
  //路徑和文件名的拼接
  public File(String pathname)

  //父路徑,子路徑
  public File(String parent, String child)

  // 獲取帶文件名的文件路徑,即 new File 構造函數傳入的路徑
  String getPath()

  String dir = “C:\\Users\\79466\\Desktop\\”;
  String name = “a.txt”;
  File file = new File(dir, name);
  // File file = new File(dir); 目錄對象

  System.out.println(file.getPath()); // 列印文件的路徑和文件名
  System.out.println(File.separator); // 列印不同系統的文件分隔符

  // 常用的文件操作 api
  file.getPath();  // 獲取帶文件名的文件路徑, C:\Users\79466\Desktop\a.txt
  file.getName();  // 獲取文件名, a.txt
  file.getAbsolutePath();  // 獲取文件的絕對路徑 C:\Users\79466\Desktop\a.txt
  file.getParent();  // 獲取文件的父路徑 C:\Users\79466\Desktop
  file.exists();  // 文件或者目錄是否存在
  file.isFile();  // 是否是一個文件
  file.isDirectory();  // 是否是一個目錄
  file.isAbsolute();  //是否是絕對路徑

  // 如果 file 是目錄,獲取文件目錄下所有的文件及目錄的名稱,操作對象是目錄,如果是文件會報錯
  String[] arr = file.list();
  for (String temp : arr) {
    System.out.println(temp);
  }

  // 創建指定目錄
  File mkdir = new File(dir + “\\xd”);
  mkdir.mkdir();  //創建一級目錄

  File mkdirs = new File(dir + “\\xd\\aa\\bb\\cc”);  // 或者 dir + “\\xd\\aa\\bb\\cc\\”
  mkdirs.mkdirs();  //創建多級目錄

  File newFile = new File(dir + “\\xxxx.txt”);
  // 如果 dir 不存在或者創建文件失敗需要捕獲異常
  try {
    newFile.createFile();
  } catch (IOException e) {
    e.printStackTrace();
  }

  newFile.delete();   //刪除操作,當前文件如果是最終的文件才可以刪除,如果是目錄,裡面還有文件,需要先刪除文件才能刪除該目錄

  File 的構造函數只是創建一個 File 實例,即使目錄錯誤也不會報錯,因為沒有對文件進行操作
  輸出流: 程式到外界設備 輸入流: 外界設備到程式
  處理數據類型分類
  字元流: 處理字元相關,如文本數據( txt 文件), Reader / Writer
  位元組流: 處理位元組相關,聲音或者圖片等二進位, InputStream/OutputStream
  兩者區別:
    位元組流以位元組(8bit)為單位,字元流以字元為單位,根據碼錶映射字元,一次可能讀多個位元組
    位元組流可以處理幾乎所有文件,字元流只能處理字元類型的數據,如果文件都是中文文本的話可以使用字元流,速度更快,更省空間
    功能不同,但是具有共性內容,抽象成4個抽象類
    字元流 Reader/Writer
    位元組流 InputStream/OutputStream

    使用的時候都不會使用抽象類進行實現,開發使用對應的子類

  位元組流:
    InputStream: 實現類及子類有 FileInputStream (用的最多, BufferedInputStream 提高性能)、 ObjectInputStream (對象輸入流,序列化的時候) 、 ByteArrayInputStream
    OutputStream 和 InputStream 一樣

    int read(byte[] buf) // 從輸入流中讀取一定數量的位元組,並將其存儲在緩衝區數組 buf 中,返回實際讀取的位元組數

    int available() // 返回這個流中有多少個位元組數,可以把 buf 數組長度定為這個

    void close() throws IOException // 關閉輸入流並釋放與該流關聯的系統資源

  FileInputStream 位元組輸入流:

    // 傳入文件所在地址
    public FileInputStream(String name) throws FileNotFoundException
    // 傳入文件對象
    public FileInputStream(File file) throws FileNotFoundException

    public static void main (String [] args) {
      File file = new File(dir, name);
      InputStream inputStream = new FileInputStream(file);
      // 讀取一個位元組
      int read = inputStream.read();
      // 位元組對應的 ASCII 碼
      System.out.println(read);
      // 強轉成字元
      System.out.println((char)read);
    }

    inputStream.skip();   // 跳過,從輸入流中跳過並丟棄 n 個位元組的數據

    byte[] buf = new byte[1024];
    int length;
    // 一次性讀取 buf.length 個位元組並放到 buf 數組中,返回類型是讀取到的位元組數
    while ((length = inputStream.read(buf)) != -1) {
      System.out.println(new String(buf, 0, length)); // 從0開始,長度是3
      System.out.println(new String(buf, 0, length, “UTF-8”));
    }

    FileOutputStream 位元組輸出流:

    構造:
    // 傳入輸出的文件地址
    public FileOutputStream(String name)

    // 傳入目標輸出的文件對象
    public FileOutputStream(File file)

    // 傳入目標輸出的文件對象,是否可以追加內容
    public FileOutputStream(File file, boolean append)

    public static void main(String [] args)  {

      String target = “a.txt”;
      InputStream inputStream = new FileInputStream(file);
      // 會創建文件,但是不會創建多級目錄
      // OutputStream outputStream = new FileOutputStream(dir + File.separator + target);
      // 不覆蓋文件,只追加數據
      OutputStream outputStream = new FileOutputStream(dir + File.separator + target, true);
      byte[] buf = new byte[1024];
      int length;

      // 一次性讀取 buf.length 個位元組並放到 buf 數組中,返回類型是讀取到的位元組數
      while ((length = inputStream.read(buf)) != -1) {
        // 按位元組數組的長度寫出
        outputStream.write(buf, 0, length);
      }
      inputStream.close();
      outputStream.close();
    }

  緩衝 Buffer :
  記憶體空間的一部分,在記憶體空間預留了一定的存儲空間,這些存儲空間用來緩衝輸入或輸出的數據,這部分空間就叫做緩衝區,緩衝區默認大小是8k,使用緩衝區暫存數據,可以減少和磁碟的交互,讀入時與磁碟連接後讀入較多的數據到緩衝區,記憶體再慢慢去消耗,

  BufferInputStream 緩衝位元組輸入流,讀取數據時,與磁碟連接一次讀取到記憶體,緩衝區滿時會再讀取下一截數據填充到緩衝區
  構造函數:
  // 對輸入流進行包裝,裡面默認的緩衝區是8k
  public BufferedInputStream(InputStream in);

  // 對輸入流進行包裝,創建具有指定緩衝區大小的 Buffer
  public BufferedInputStream(InputStream in, int size);

  方法:
  // 從輸入流中讀取一個位元組
  public int read();

  // 從位元組輸入流中給定偏移量處開始將各位元組讀取到指定的 byte 數組中
  public int read(byte[] buf, int off, int len);

  // 關閉資源,關閉這個流即可, InputStream 會在裡面被關閉
  void close();

  BufferOutputStream 緩衝位元組輸出流,當緩衝區滿時,會自動寫出到磁碟
  構造同上
  常用方法:
  // 向輸出流中輸出一個位元組
  public void write(int b);

  // 將指定 byte 數組中從偏移量 off 開始的 len 個位元組寫入緩衝的輸出流
  public void write(byte[] buf, int off, int len);

  // 刷新此緩衝的輸出流,強制使所有緩衝的輸出位元組被寫出到底層輸出流中,當緩衝區的大小未滿時,需要手動刷到磁碟
  public void flush();

  // 關閉釋放資源, OutputStream 會在裡面被關閉, JDK7 新特性 try (在這裡聲明的 流 會自動關閉){}
  void close();

  緩衝輸入輸出流進行文件拷貝:
  try {
    FileInputStream fis = new FileInputStream(“C:\\Users\\79466\\Desktop\\test\\a.txt”);
    BufferedInputStream bis = new BufferedInputStream(fis);

    FileOutputStream fos = new FileOutputStream(“C:\\Users\\79466\\Desktop\\test\\copy.txt”);
    BufferedOutputStream bos = new BufferedOutputStream(fos);
    int size;
    byte[] buf = new byte[1024];
    

    while (( size = bis.read(buf)) != -1) {
      // 將位元組輸出流寫到緩衝區裡面,等緩衝區滿後自動寫出
      bos.write(buf, 0, size);
    }
    // 刷新此緩衝區的輸出流,才可以保證數據全部輸出完成,關閉的時候也會進行刷新,寫了也不要緊,也就是多刷一次, close 方法的關閉前會先進行刷新
    // bos.flush();

    // 內部會關掉 InputStream 和 OutputStream
    bis.close();
    // close 源碼裡面會有 flush ,可以不用單獨調用 flush ,jdk7 之後 close 源碼里的 try 裡面聲明了 OutputStream ,會自動關閉 outputStream 流
    bos.close();
    } catch (Exception e) {
      e.printStackTrace();
  }

  流的關閉順序,後開的先關,如果A依賴B,先關閉B

  IO 的異常處理:
  大部分公司的做法: 在 finally 裡面進行關閉
  catch (Exception e) {
    e.printStackTrace();
  } finally {
    if(bis != null) {
      try {
        bis.close();
      }catch () {
        e.printStackTrace();
      }finally {
        if(bos != null) {
          try {
            bos.close();
          }catch (Exception e) {
            e.printStackTrace();
          }
        }
      }
    }
  }

  jdk7 之後的做法: try-with-resource
  try 裡面聲明的 OutputStream 和 InputStream 會自動關閉, jdk7 之後的 InputStream 都實現了 AutoCloseable
  在 try 裡面定義多個資源,關閉的順序是最後在 try() 定義的資源最先關閉

  try (
    FileInputStream fis = new FileInputStream(“C:\\Users\\79466\\Desktop\\test\\a.txt”);
    BufferedInputStream bis = new BufferedInputStream(fis);
    FileOutputStream fos = new FileOutputStream(“C:\\Users\\79466\\Desktop\\test\\copy.txt”);
    BufferedOutputStream bos = new BufferedOutputStream(fos);)  {

    int size;
    byte[] buf = new byte[1024];

    while(( size = bis.read(buf)) != -1 ) {
      bos.write(buf, 0, size);
    }

    bos.flush();
  } catch (Exception e) {
      e.printStackTrace();
  }