Java 檔案 IO 操作

陌小但發表於2020-10-07

檔案:文字、圖片、視訊、程式等儲存在計算機上

檔案目錄:資料夾,管理檔案, linux 下有許可權、操作符、使用者組、使用者等

       路徑分割符:

  \ 表示 windows 系統檔案目錄分割符
  / 表示 mac/linux 下的路徑分割符

  java 程式碼在 windows 下寫某個檔案的話需要下面的方式

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

  java 程式碼在 linux 或者 Mac 下寫某個檔案的話需要下面的方式

  usr/local/soft/sd.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 緩衝位元組輸出流,當緩衝區滿時,會自動寫出到磁碟
  構造同  BufferInputStream 
  常用方法:
  // 向輸出流中輸出一個位元組
  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();
  }

 

相關文章