傳智黑馬java基礎學習——day23(位元組流、字元流)

高數老師發表於2019-01-24

第23天 IO流

今日內容介紹

  1. 位元組流
  2. 字元流
  1. 位元組流

在前面的學習過程中,我們一直都是在操作檔案或者資料夾,並沒有給檔案中寫任何資料。現在我們就要開始給檔案中寫資料,或者讀取檔案中的資料。

    1. 位元組輸出流OutputStream

OutputStream此抽象類,是表示輸出位元組流的所有類的超類。操作的資料都是位元組,定義了輸出位元組流的基本共性功能方法。

輸出流中定義都是寫write方法,如下圖:

      1. FileOutputStream類

OutputStream有很多子類,其中子類FileOutputStream可用來寫入資料到檔案。

FileOutputStream類,即檔案輸出流,是用於將資料寫入 File的輸出流。

  1. 構造方法

      1. FileOutputStream類寫入資料到檔案中
  1. 將資料寫到檔案中,程式碼演示:

public class FileOutputStreamDemo {

public static void main(String[] args) throws IOException {

//需求:將資料寫入到檔案中。

//建立儲存資料的檔案。

File file = new File("c:\\file.txt");

//建立一個用於操作檔案的位元組輸出流物件。一建立就必須明確資料儲存目的地。

//輸出流目的是檔案,會自動建立。如果檔案存在,則覆蓋。

FileOutputStream fos = new FileOutputStream(file);

//呼叫父類中的write方法。

byte[] data = "abcde".getBytes();

fos.write(data);

//關閉流資源。

fos.close();

}

}

 

      1. 給檔案中續寫和換行

我們直接new FileOutputStream(file)這樣建立物件,寫入資料,會覆蓋原有的檔案,那麼我們想在原有的檔案中續寫內容怎麼辦呢?

繼續查閱FileOutputStream的API。發現在FileOutputStream的建構函式中,可以接受一個boolean型別的值,如果值true,就會在檔案末位繼續新增。

  1. 構造方法

  1. 給檔案中續寫資料和換行,程式碼演示:

public class FileOutputStreamDemo2 {

public static void main(String[] args) throws Exception {

File file = new File("c:\\file.txt");

FileOutputStream fos = new FileOutputStream(file, true);

String str = "\r\n"+"itcast";

fos.write(str.getBytes());

fos.close();

}

}

      1. IO異常的處理

在前面編寫程式碼中都發生了IO的異常。我們在實際開發中,對異常時如何處理的,我們來演示一下。

public class FileOutputStreamDemo3 {

public static void main(String[] args) {

File file = new File("c:\\file.txt");

//定義FileOutputStream的引用

FileOutputStream fos = null;

try {

//建立FileOutputStream物件

fos = new FileOutputStream(file);

//寫出資料

fos.write("abcde".getBytes());

} catch (IOException e) {

System.out.println(e.toString() + "----");

} finally {

//一定要判斷fos是否為null,只有不為null時,才可以關閉資源

if (fos != null) {

try {

fos.close();

} catch (IOException e) {

throw new RuntimeException("");

}

}

}

}

}

    1. 位元組輸入流InputStream

通過前面的學習,我們可以把記憶體中的資料寫出到檔案中,那如何想把記憶體中的資料讀到記憶體中,我們通過InputStream可以實現。InputStream此抽象類,是表示位元組輸入流的所有類的超類。,定義了位元組輸入流的基本共性功能方法。

  1. int read():讀取一個位元組並返回,沒有位元組返回-1.
  2. int read(byte[]): 讀取一定量的位元組數,並儲存到位元組陣列中,返回讀取到的位元組數。
      1. FileInputStream類

InputStream有很多子類,其中子類FileInputStream可用來讀取檔案內容。

FileInputStream 從檔案系統中的某個檔案中獲得輸入位元組。

  1. 構造方法

      1. FileInputStream類讀取資料read方法

在讀取檔案中的資料時,呼叫read方法,實現從檔案中讀取資料

  1. 從檔案中讀取資料,程式碼演示:

public class FileInputStreamDemo {

public static void main(String[] args) throws IOException {

File file = new File("c:\\file.txt");

//建立一個位元組輸入流物件,必須明確資料來源,其實就是建立位元組讀取流和資料來源相關聯。

FileInputStream fis = new FileInputStream(file);

//讀取資料。使用 read();一次讀一個位元組。

int ch = 0;

while((ch=fis.read())!=-1){

System.out.pr }intln("ch="+(char)ch);

 

// 關閉資源。

fis.close();

}

}

 

      1. 讀取資料read(byte[])方法

在讀取檔案中的資料時,呼叫read方法,每次只能讀取一個,太麻煩了,於是我們可以定義陣列作為臨時的儲存容器,這時可以呼叫過載的read方法,一次可以讀取多個字元。

public class FileInputStreamDemo2 {

public static void main(String[] args) throws IOException {

/*

 * 演示第二個讀取方法, read(byte[]);

 */

File file = new File("c:\\file.txt");

// 建立一個位元組輸入流物件,必須明確資料來源,其實就是建立位元組讀取流和資料來源相關聯。

FileInputStream fis = new FileInputStream(file);

//建立一個位元組陣列。

byte[] buf = new byte[1024];//長度可以定義成1024的整數倍。

int len = 0;

while((len=fis.read(buf))!=-1){

System.out.println(new String(buf,0,len));

}

fis.close();

}

}

    1. 位元組流練習

既然會了檔案的讀和寫操作了,那麼我們就要在這個基礎上進行更為複雜的操作。使用讀寫操作完成檔案的複製。

      1. 複製檔案

原理;讀取一個已有的資料,並將這些讀到的資料寫入到另一個檔案中。

public class CopyFileTest {

public static void main(String[] args) throws IOException {

//1,明確源和目的。

File srcFile = new File("c:\\YesDir\test.JPG");

File destFile = new File("copyTest.JPG");

 

//2,明確位元組流 輸入流和源相關聯,輸出流和目的關聯。

FileInputStream fis = new FileInputStream(srcFile);

FileOutputStream fos = new FileOutputStream(destFile);

 

//3, 使用輸入流的讀取方法讀取位元組,並將位元組寫入到目的中。

int ch = 0;

while((ch=fis.read())!=-1){

fos.write(ch);

}

//4,關閉資源。

fos.close();

fis.close();

}

}

上述程式碼輸入流和輸出流之間是通過ch這個變數進行資料交換的。

上述複製檔案有個問題,每次都從原始檔讀取一個,然後在寫到指定檔案,接著再讀取一個字元,然後再寫一個,一直這樣下去。效率極低。

      1. 緩衝陣列方式複製檔案

上述程式碼複製檔案效率太低了,並且頻繁的從檔案讀資料,和寫資料,能不能一次多把檔案中多個資料都讀進內容中,然後在一次寫出去,這樣的速度一定會比前面程式碼速度快。

public class CopyFileByBufferTest {

public static void main(String[] args) throws IOException {

File srcFile = new File("c:\\YesDir\test.JPG");

File destFile = new File("copyTest.JPG");

// 明確位元組流 輸入流和源相關聯,輸出流和目的關聯。

FileInputStream fis = new FileInputStream(srcFile);

FileOutputStream fos = new FileOutputStream(destFile);

//定義一個緩衝區。

byte[] buf = new byte[1024];

int len = 0;

while ((len = fis.read(buf)) != -1) {

fos.write(buf, 0, len);// 將陣列中的指定長度的資料寫入到輸出流中。

}

// 關閉資源。

fos.close();

fis.close();

}

}

  1. 字元

經過前面的學習,我們基本掌握的檔案的讀寫操作,在操作過程中位元組流可以操作所有資料,可是當我們操作的檔案中有中文字元,並且需要對中文字元做出處理時怎麼辦呢?

    1. 位元組流讀取字元的問題

通過以下程式讀取帶有中檔案的檔案。  

public class CharStreamDemo {

public static void main(String[] args) throws IOException {

//給檔案中寫中文

writeCNText();

//讀取檔案中的中文

readCNText();

}

//讀取中文

public static void readCNText() throws IOException {

FileInputStream fis = new FileInputStream("c:\\cn.txt");

int ch = 0;

while((ch = fis.read())!=-1){

System.out.println(ch);

}

}

//寫中文

public static void writeCNText() throws IOException {

FileOutputStream fos = new FileOutputStream("c:\\cn.txt");

fos.write("a傳智播客歡迎你".getBytes());

fos.close();

}

}

上面程式在讀取含有中文的檔案時,我們並沒有看到具體的中文,而是看到一些數字,這是什麼原因呢?既然看不到中文,那麼我們如何對其中的中文做處理呢?要解決這個問題,我們必須研究下字元的編碼過程。

    1. 字元編碼表

我們知道計算機底層資料儲存的都是二進位制資料,而我們生活中的各種各樣的資料,如何才能和計算機中儲存的二進位制資料對應起來呢?

這時老美他們就把每一個字元和一個整數對應起來,就形成了一張編碼表,老美他們的編碼表就是ASCII表。其中就是各種英文字元對應的編碼。

編碼表:其實就是生活中字元和計算機二進位制的對應關係表。

1、ascii: 一個位元組中的7位就可以表示。對應的位元組都是正數。0-xxxxxxx

2、iso-8859-1:拉丁碼錶 latin,用了一個位元組用的8位。1-xxxxxxx  負數。

3、GB2312:簡體中文碼錶。包含6000-7000中文和符號。用兩個位元組表示。兩個位元組第一個位元組是負數,第二個位元組可能是正數

   GBK:目前最常用的中文碼錶,2萬的中文和符號。用兩個位元組表示,其中的一部分文字,第一個位元組開頭是1,第二位元組開頭是0

    GB18030:最新的中文碼錶,目前還沒有正式使用。

  1. unicode:國際標準碼錶:無論是什麼文字,都用兩個位元組儲存。
  1. Java中的char型別用的就是這個碼錶。char c = 'a';佔兩個位元組。
  2. Java中的字串是按照系統預設碼錶來解析的。簡體中文版 字串預設的碼錶是GBK。

5、UTF-8:基於unicode,一個位元組就可以儲存資料,不要用兩個位元組儲存,而且這個碼錶更加的標準化,在每一個位元組頭加入了編碼資訊(後期到api中查詢)。

能識別中文的碼錶:GBK、UTF-8;正因為識別中文碼錶不唯一,涉及到了編碼解碼問題。

對於我們開發而言;常見的編碼 GBK  UTF-8  ISO-8859-1

文字--->(數字) :編碼。 “abc”.getBytes()  byte[]

(數字)--->文字  : 解碼。 byte[] b={97,98,99}  new String(b)

    1. 字元輸入流Reader

上述程式中我們讀取擁有中文的檔案時,使用的位元組流在讀取,那麼我們讀取到的都是一個一個位元組。只要把這些位元組去查閱對應的編碼表,就能夠得到與之對應的字元。API中是否給我們已經提供了讀取相應字元的功能流物件,Reader,讀取字元流的抽象超類。

  1. read():讀取單個字元並返回
  2. read(char[]):將資料讀取到陣列中,並返回讀取的個數。
      1. FileReader類

查閱FileInputStream的API,發現FileInputStream 用於讀取諸如影像資料之類的原始位元組流。要讀取字元流,請考慮使用 FileReader。

開啟FileReader的API介紹。用來讀取字元檔案的便捷類。此類的構造方法假定預設字元編碼和預設位元組緩衝區大小都是適當的

  1. 構造方法

      1. FileReader讀取包含中文的檔案
  1. 使用FileReader讀取包含中文的檔案

public class CharStreamDemo {

public static void main(String[] args) throws IOException {

//給檔案中寫中文

writeCNText();

//讀取檔案中的中文

readCNText();

}

//讀取中文

public static void readCNText() throws IOException {

FileReader fr = new FileReader("D:\\test\\cn.txt");

int ch = 0;

while((ch = fr.read())!=-1){

//輸出的字元對應的編碼值

System.out.println(ch);

//輸出字元本身

System.out.println((char)ch);

}

}

//寫中文

public static void writeCNText() throws IOException {

FileOutputStream fos = new FileOutputStream("D:\\test\\cn.txt");

fos.write("a傳智播客歡迎你".getBytes());

fos.close();

}

}

    1. 字元輸出流Writer

既然有專門用於讀取字元的流物件,那麼肯定也有寫的字元流物件,查閱API,發現有一個Writer類,Writer是寫入字元流的抽象類。其中描述了相應的寫的動作。

      1. FileWriter類

查閱FileOutputStream的API,發現FileOutputStream 用於寫入諸如影像資料之類的原始位元組的流。要寫入字元流,請考慮使用 FileWriter。

開啟FileWriter的API介紹。用來寫入字元檔案的便捷類。此類的構造方法假定預設字元編碼和預設位元組緩衝區大小都是可接受的。

  1. 構造方法

      1. FileWriter寫入中文到檔案中
  1. 寫入字元到檔案中,先進行流的重新整理,再進行流的關閉。

public class FileWriterDemo {

public static void main(String[] args) throws IOException {

//演示FileWriter 用於操作檔案的便捷類。

FileWriter fw = new FileWriter("d:\\text\\fw.txt");

fw.write("你好謝謝再見");//這些文字都要先編碼。都寫入到了流的緩衝區中。

fw.flush();

fw.close();

}

}

    1. flush()和close()的區別?

flush():將流中的緩衝區緩衝的資料重新整理到目的地中,重新整理後,流還可以繼續使用。

close():關閉資源,但在關閉前會將緩衝區中的資料先重新整理到目的地,否則丟失資料,然後在關閉流。流不可以使用。如果寫入資料多,一定要一邊寫一邊重新整理,最後一次可以不重新整理,由close完成重新整理並關閉。

    1. 字元流練習
      1. 複製文字檔案

練習:複製文字檔案。

思路:

1,既然是文字涉及編碼表。需要用字元流。

2,操作的是檔案。涉及硬碟。

3,有指定碼錶嗎?沒有,預設就行。

操作的是檔案,使用的 預設碼錶。使用哪個字元流物件。直接使用字元流操作檔案的便捷類。FileReader  FileWriter

 

public class CopyTextFileTest {

public static void main(String[] args) throws IOException {

copyTextFile();

}

public static void copyTextFile() throws IOException {

//1,明確源和目的。

FileReader fr = new FileReader("c:\\cn.txt");

FileWriter fw = new FileWriter("c:\\copy.txt");

//2,為了提高效率。自定義緩衝區陣列。字元陣列。

char[] buf = new char[1024];

int len = 0;

while((len=fr.read(buf))!=-1){

fw.write(buf,0,len);

}

/*2,迴圈讀寫操作。效率低。

int ch = 0;

while((ch=fr.read())!=-1){

fw.write(ch);

}

*/

//3,關閉資源。

fw.close();

fr.close();

}

}

 

  1. 總結
    1. 知識點總結
  1. IO流的分類

|- 位元組流

|- 位元組輸入流 InputStream 抽象類

|-  FileInputStream 操作檔案的位元組輸入流

|- 位元組輸出流 OuputStream抽象類

    |- FileOutputStream 操作檔案的位元組輸出流

|- 字元流

|- 字元輸入流 Reader抽象類

|- InputStreamReader 輸入操作的轉換流

|- FileReader 用來操作檔案的字元輸入流(簡便的流)

|- 字元輸出流 Writer抽象類

|- OutputStreamWriter 輸出操作的轉換流

|- FileWriter 用來操作檔案的字元輸出流(簡便的流)

相關文章