JavaIO流(一)-位元組輸入流與字元輸入流

bingfeng發表於2020-12-15

IO流詳解

一、輸入流

位元組輸入流

FileInputSteam

1、構造方法:
public FileInputStream(File file) {}
public FileInputStream(FileDescriptor fdObj){}
public FileInputStream(String name){}
2、read方法:
// 每次讀取一個位元組
public int read(){}

// 讀取b.length個位元組到byte陣列中
public int read(byte b[]){}

// 從輸入流中讀取len個位元組到位元組陣列中
public int read(byte b[], int off, int len){}
3、檔案讀取:
1、read()
每次讀取一個位元組資料,返回位元組數,如果到達檔案末尾,返回-1。

文字

abc

public static void method_01(String filePath) throws IOException {
  FileInputStream inputStream = null;
  try {
    inputStream = new FileInputStream(filePath);
        // 每次讀取一個位元組
    for (int i = 0; i < 4; i++) {
      int read = inputStream.read();
      System.out.println(read);
    }
  } catch (FileNotFoundException e) {
    e.printStackTrace();
  } finally {
    if (null != inputStream) {
      // 關閉IO流
      inputStream.close();
    }
  }
}

執行結果:

97
98
99
-1

從執行結果可以看出,前三次讀取到了資料,返回了對應的ASCII碼,當讀取到檔案末尾的時候,則返回-1。


2、read(byte[] b)
讀入緩衝區的位元組總數,如果到達檔案末尾,則返回-1。

文字:

abcdefg

宣告一個大於真實資料的byte陣列讀取資料。

public static void method_02(String filePath) throws IOException {

  FileInputStream inputStream = null;
  try {
    inputStream = new FileInputStream(filePath);

    // 宣告的長度大於真實資料長度
    byte[] bytes = new byte[20];

    int length = inputStream.read(bytes);

    System.out.println("位元組陣列長度:" + bytes.length + "  讀取到的資料位元組長度:" + length);

    for (byte b : bytes) {
      System.out.print(b + " | ");
    }
  } catch (FileNotFoundException e) {
    e.printStackTrace();
  } finally {
    if (null != inputStream) {
      inputStream.close();
    }
  }
}

執行結果:

位元組陣列長度:20  讀取到的資料位元組長度:7
97 | 98 | 99 | 100 | 101 | 102 | 103 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 

可以看出,當我們byte陣列的長度大於位元組陣列的真實長度之後,那麼後面的空間全部使用0做補位,這也恰恰反映了另外一個問題,我們在使用byte[]陣列讀取檔案的時候,千萬不要說我設定足夠大的長度,就可以高枕無憂提高讀取效率,如果遇到小檔案,那麼也是很容易造成效率低下的。


3、read(byte b[], int off, int len)
讀入緩衝區的位元組總數,如果讀取到檔案末尾,則返回-1;

文字:

abcdefg

宣告一個固定大小的byte陣列,迴圈讀取資料

我們的文字有七個位元組,宣告瞭一個2個長度的陣列,應該迴圈四次,第五次讀取的時候返回-1。

public static void method_03(String filePath) throws IOException {

  FileInputStream inputStream = null;
  try {
    inputStream = new FileInputStream(filePath);

    // 宣告2個長度
    byte[] bytes = new byte[2];

    int i = 0;
    while (i < 5) {
      int length = inputStream.read(bytes, 0, bytes.length);
      System.out.println("第" + (i + 1) + "次讀取,length: " + length);
      System.out.println("開始輸出:");
      for (int j = 0; j < length; j++) {
        System.out.print(bytes[j] + " | ");
      }
      System.out.println();
      i++;
    }
  } catch (FileNotFoundException e) {
    e.printStackTrace();
  } finally {
    if (null != inputStream) {
      inputStream.close();
    }
  }
}

執行結果:

第1次讀取,length: 2
開始輸出:
97 | 98 | 
第2次讀取,length: 2
開始輸出:
99 | 100 | 
第3次讀取,length: 2
開始輸出:
101 | 102 | 
第4次讀取,length: 1
開始輸出:
103 | 
第5次讀取,length: -1
開始輸出:

注意:

可能有的朋友會遇到我之前遇到的問題,他的文字里面寫了漢字或者標點符號,會出現亂碼,我們給文字最後再追加一箇中文,並且把每次讀取到的byte陣列轉換成String進行輸出,看會出現什麼情況。

public static void method_03(String filePath) throws IOException {
  FileInputStream inputStream = null;
  try {
    inputStream = new FileInputStream(filePath);

    byte[] bytes = new byte[2];

    int i = 0;
    while (i < 5) {
      inputStream.read(bytes, 0, bytes.length);
      
      // 將byte[]轉換成string
      String s = new String(bytes, StandardCharsets.UTF_8);
      System.out.println(s);
      i++;
    }
  } catch (FileNotFoundException e) {
    e.printStackTrace();
  } finally {
    if (null != inputStream) {
      inputStream.close();
    }
  }
}

結果:

ab
cd
ef
g�
��

剛開始腦子抽了,感覺這是什麼問題,怎麼會亂碼呢,如果稍微上點心的都會發現,中文佔3個byte位元組,你用2個儲存那鐵定亂碼呀。那麼你肯定想過那我把陣列宣告大一點不就好了,如果你這麼想過,那你可能還不知道社會的險惡。

那麼到底怎麼辦呢?真的就沒辦法了嗎?接下來我們用一個例子來學習如何解決這種問題。

一個?:
將文字中的內容讀取出來,輸出到控制檯。

既然我們知道,上面的亂碼是因為英文和中文佔用的位元組數不同引起的,那我們要是知道了整個檔案佔用的位元組長度,那麼不就一次性可以讀取出來了。恰好FileInputStream提供了這樣一個方法(available),讓我們可以獲取到整個檔案所佔用的位元組數。

public static void printConsole(String filePath) throws IOException {

  FileInputStream inputStream = null;
  try {
    inputStream = new FileInputStream(filePath);

       // 獲取到整個文字佔用的整個位元組數
    int available = inputStream.available();

    // 宣告陣列
    byte[] bytes = new byte[available];

    // 讀取資料
    int readLength = inputStream.read(bytes, 0, available);
    String s = new String(bytes, StandardCharsets.UTF_8);

    System.out.println("讀取到的長度:" + readLength + "  available:" + available);
    System.out.println("讀取到的內容: " + s);
  } catch (FileNotFoundException e) {
    e.printStackTrace();
  } finally {
    if (null != inputStream) {
      inputStream.close();
    }
  }
}

結果:

讀取到的長度:30  available:30
讀取到的內容: abcdef一個程式設計師的成長

這樣的話,我們就可以讀取到文字中完整的內容了。只有瞭解了這些,那麼才會瞭解我們的寫檔案下載的時候為什麼要判斷 讀取到的位元組數 !=-1 這樣的操作,不然真的很難記住。


字元輸入流

FileReader

1、構造方法:
public FileReader(String fileName){};

public FileReader(File file){};

public FileReader(FileDescriptor fd){};
2、read方法:
public int read(){};

public int read(char cbuf[], int offset, int length){};

public int read(char cbuf[]){};
3、檔案讀取:

FileReader的read方法讀取出來的是一個獨立的字元(char),所以面對英文和中文的混合,我們不會因為佔用位元組的不同從而導致出現亂碼的情況。

文字:

abcdef一個程式設計師的成長

讀取內容的程式碼:

public static void method_01(String filePath) throws IOException {

  FileReader fr = null;
  try {
    fr = new FileReader(filePath);

    int c;
    // 每次讀取一個字元,等於-1即表示檔案讀取到末尾
    while ((c = fr.read()) != -1) {
      System.out.println((char) c);
    }
  } catch (FileNotFoundException e) {
    e.printStackTrace();
  } finally {
    if (null != fr) {
      fr.close();
    }
  }

結果:

a
b
c
d
e
f
一
個
程
序
員
的
成
長

根據結果可以看出,read()讀取出來的每次就是一個單獨的字元,那麼另外兩個read方法跟位元組流讀取都是一樣的。

更多內容請關注微信公眾號:

相關文章