天天看點

系統學習 Java IO (十三)----字元讀寫 Reader/Writer 及其常用子類

目錄:系統學習 Java IO---- 目錄,概覽

Reader

Reader 類是 Java IO API 中所有 Reader 子類的基類。 Reader 類似于 InputStream ,除了它是基于字元而不是基于位元組的。 換句話說, Reader 用于讀取文本,而 InputStream 用于讀取原始位元組。

Writer

Writer 類是 Java IO API 中所有 Writer 子類的基類。 Writer 就像一個 OutputStream ,除了它是基于字元而不是基于位元組的。 換句話說,Writer 用于寫入文本,而 OutputStream 用于寫入原始位元組。

Writer 通常連接配接到某些資料目标,如檔案,字元數組,網絡套接字等。

Unicode中的字元

許多應用程式使用 UTF(UTF-8或UTF-16)來存儲文本資料。 可能需要一個或多個位元組來表示 UTF-8 中的單個字元。 在 UTF-16 中,每個字元需要 2 個位元組來表示。 是以,在讀取或寫入文本資料時,資料中的單個位元組可能與 UTF 中的一個字元不對應。 如果隻是通過 InputStream 一次讀取或寫入 UTF-8 資料的一個位元組,并嘗試将每個位元組轉換為字元,可能不會得到正确的文本。

Reader 類能夠将位元組解碼為字元。 隻需要告訴Reader 要解碼的字元集。 這是在執行個體化 Reader 時執行的(當執行個體化其中一個子類時)。 通常會直接使用 Reader 子類而不是 Reader。Writer 同理。

讀取

部分方法如下:

方法 描述
void mark(int readAheadLimit) 标記流中的目前位置。
int read() 讀取單個字元。
int read(char[] cbuf) 将字元讀入數組。
abstract int read(char[] cbuf, int off, int len) 将字元讀入數組的某一部分。
int read(CharBuffer target) 試圖将字元讀入指定的字元緩沖區。
boolean ready() 判斷是否準備讀取此流。

具體的使用需要參考對應的子類。

和 InputStream 類似,如果 read() 方法傳回 -1 ,則 Reader 中沒有更多資料要讀取,并且可以關閉它。-1 作為 int 值,而不是 -1 作為 byte 或 char 值。

将位元組流包裝成字元流 InputStreamReader/OutputStreamWrite

InputStreamReade

InputStreamReade 類用于包裝 InputStream ,進而将基于位元組的輸入流轉換為基于字元的 Reader 。 換句話說,InputStreamReader 将 InputStream 的位元組解釋為文本而不是數字資料,是位元組流通向字元流的橋梁。

為了達到最高效率,可要考慮在 BufferedReader 内包裝 InputStreamReader。例如:

BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
           

通常用于從檔案(或網絡連接配接)中讀取字元,其中位元組表示文本。 例如,一個文本檔案,其中字元編碼為 UTF-8 。 可以使用InputStreamReader 來包裝 FileInputStream 以讀取此類檔案。

一個示例如下:

InputStream inputStream = new FileInputStream("D:\\test\\1.txt");
Reader inputStreamReader = new InputStreamReader(inputStream);

int data = inputStreamReader.read();
while (data != -1) {
	char c = (char) data;
	System.out.print(c);
	data = inputStreamReader.read();
}
inputStreamReader.close();
           

首先建立一個 FileInputStream ,然後将其包裝在 InputStreamReader 中。 其次,該示例通過 InputStreamReader 從檔案中讀取所有字元.

注意:為清楚起見,此處已跳過正确的異常處理。 要了解有關正确異常處理的更多資訊,請轉至目錄的 Java IO 異常處理。

指定字元集

底層 InputStream 中的字元将使用某些字元編碼進行編碼。 此字元編碼稱為字元集,Charset。 兩種常用字元集是 ASCII 和 UTF8(在某些情況下為UTF-16)。

預設字元集可能因為環境不同而不同,是以建議告訴 InputStreamReader 執行個體 InputStream 中的字元用什麼字元集進行編碼。 這可以在 InputStreamReader 構造函數中指定,可以隻提供字元集的名字字元串,在底層會調用

Charset.forName("UTF-8")

進行轉換的。 以下是設定 InputStreamReader 使用的字元集的示例:

InputStream inputStream = new FileInputStream("D:\\test\\1.txt");
Reader inputStreamReader = new InputStreamReader(inputStream, "UTF-8");
           
關閉 InputStreamReader

同樣建議使用 try with resources 來關閉流。同樣,隻要關閉最外層的包裝流,裡面的流會被系統自動關閉。

try(InputStreamReader inputStreamReader =
    new InputStreamReader(input)){
    int data = inputStreamReader.read();
    while(data != -) {
        System.out.print((char) data));
        data = inputStreamReader.read();
    } 
}
           

OutputStreamWriter

OutputStreamWriter 類用于包裝 OutputStream ,進而将基于位元組的輸出流轉換為基于字元的 Writer 。

如果需要将字元寫入檔案,OutputStreamWriter 非常有用,例如編碼為 UTF-8 或 UTF-16。 然後可以将字元(char 值)寫入 OutputStreamWriter ,它将正确編碼它們并将編碼的位元組寫入底層的 OutputStream 。

一個簡單的示例如下:

OutputStream outputStream = new FileOutputStream("D:\\test\\1.txt");
Writer outputStreamWriter = new OutputStreamWriter(outputStream);
outputStreamWriter.write("Hello OutputStreamWriter");
outputStreamWriter.close();
           

同 InputStreamReader,OutputStreamWriter 也可以使用指定的字元集輸出,如:

Writer outputStreamWriter = new OutputStreamWriter(outputStream, "UTF-8");
           
關閉 OutputStreamReader

參考關閉 InputStreamReader 或 Java IO 異常處理。

讀寫檔案 FileReader/FileWriter

FileReader

FileReader類使得可以将檔案的内容作為字元流讀取。 它的工作方式與 FileInputStream 非常相似,隻是 FileInputStream 讀取位元組,而 FileReader 讀取字元。 換句話說,FileReader 旨在讀取文本。 取決于字元編碼方案,一個字元可以對應于一個或多個位元組。

FileReader 假定您要使用運作應用程式的計算機的預設字元編碼來解碼檔案中的位元組。 這可能并不總是你想要的,但是不能改變它!

如果要指定其他字元解碼方案,請不要使用 FileReader 。 而是在 FileInputStream 上使用 InputStreamReader 。 InputStreamReader 允許您指定在讀取基礎檔案中的位元組時使用的字元編碼方案。

FileWriter

FileWriter 類可以将字元寫入檔案。 在這方面它的工作原理與 FileOutputStream 非常相似,隻是 FileOutputStream 是基于位元組的,而 FileWriter 是基于字元的。 換句話說,FileWriter 用于寫文本。 一個字元可以對應于一個或多個位元組,這取決于使用的字元編碼方案。

建立 FileWriter 時,可以決定是否要覆寫具有相同名稱的任何現有檔案,或者隻是要追加内容到現有檔案。 可以通過選擇使用的 FileWriter 構造函數來決定。

  • FileWriter(File file, boolean append): 根據給定的 File 對象構造一個 FileWriter 對象。 示例如下:
Writer fileWriter = new FileWriter("c:\\data\\output.txt", true);  // 追加模式
Writer fileWriter = new FileWriter("c:\\data\\output.txt", false); // 預設情況,直接覆寫原檔案
           

注意,隻要成功 new 了一個 FileWriter 對象,沒有指定是追加模式的話,那不管有沒有調用 write() 方法,都會清空檔案内容。

下面是一個讀和寫的例子:

public class FileRW {
    public static void main(String[] args) throws IOException {
        // 預設是覆寫模式
        File file = new File("D:\\test\\1.txt");
        Writer writer1 = new FileWriter(file);
        writer1.write("string from writer1, ");
        writer1.close();

        Writer writer2 = new FileWriter(file, true);
        writer2.write("append content from writer2");
        writer2.close();


        Reader reader = new FileReader(file);
        int data = reader.read();
        while (data != -1) {
            // 将會輸出 string from writer1, append content from writer2
            System.out.print((char) data);
            data = reader.read();
        }
        reader.close();
    }
}
           

注意:為清楚起見,此處已跳過正确的異常處理。 要了解有關正确異常處理的更多資訊,請轉至Java IO異常處理。

其他行為和 InputStreamReader 差不多,就不展開講了。

PipedReader/PipedWriter

讀寫管道 PipedReader 和 PipedWriter

PipedReader 類使得可以将管道的内容作為字元流讀取。 是以它的工作方式與 PipedInputStream 非常相似,隻是PipedInputStream 是基于位元組的,而不是基于字元的。 換句話說,PipedReader 旨在讀取文本。PipedWriter 同理。

構造器
PipedReader() 建立尚未連接配接的 PipedReader。
PipedReader(int pipeSize) 建立一個尚未連接配接的 PipedReader,并對管道緩沖區使用指定的管道大小。
PipedReader(PipedWriter src) 建立直接連接配接到傳送 PipedWriter src 的 PipedReader。
PipedReader(PipedWriter src, int pipeSize) 建立一個 PipedReader,使其連接配接到管道 writer src,并對管道緩沖區使用指定的管道大小。
PipedWriter() 建立一個尚未連接配接到傳送 reader 的傳送 writer。
PipedWriter(PipedReader snk) 建立傳送 writer,使其連接配接到指定的傳送 reader。
讀寫之前,必須先建立連接配接

PipedReader 必須連接配接到 PipedWriter 才可以讀 ,PipedWriter 也必須始終連接配接到 PipedReader 才可以寫。就是說讀寫之前,必須先建立連接配接,有兩種方式可以建立連接配接。

  1. 通過構造器建立,僞代碼如

    Piped piped1 = new Piped(piped2);

  2. 調用其中一個的 connect() 方法,僞代碼如

    Piped1.connect(Piped2);

并且通常,PipedReader 和 PipedWriter 由不同的線程使用。 注意隻有一個 PipedReader 可以連接配接到同一個 PipedWriter ,一個示例如下:

PipedWriter writer = new PipedWriter();
PipedReader reader = new PipedReader(writer);

writer.write("string form pipedwriter");
writer.close();

int data = reader.read();
while (data != -1) {
        System.out.print((char) data); // string form pipedwriter
        data = reader.read();
    }
reader.close();
           

注意:為清楚起見,這裡忽略了正确的 IO 異常處理,并且沒有使用不同線程,不同線程操作請參考 PipedInputStream 。

正如在上面的示例中所看到的,PipedReader 需要連接配接到 PipedWriter 。 當這兩個字元流連接配接時,它們形成一個管道。 要了解有關 Java IO 管道的更多資訊,請參考 管道流 PipedInputStream 部分。

讀寫字元數組 CharArrayReader/CharArrayWriter

ByteArrayInputStream/ByteArrayOutputStream 是對位元組數組處理,CharArrayReader/CharArrayWriter 則是對字元數組進行處理,其用法是基本一緻的,是以這裡略微帶過。

CharArrayReader

CharArrayReader 類可以将 char 數組的内容作為字元流讀取。

隻需将 char 數組包裝在 CharArrayReader 中就可以很友善的生成一個 Reader 對象。

CharArrayWriter

CharArrayWriter 類可以通過 Writer 方法(CharArrayWriter是Writer的子類)編寫字元,并将寫入的字元轉換為 char 數組。

在寫入所有字元時,CharArrayWriter 上調用 toCharArray() 能很友善的生成一個字元數組。

兩個類的構造函數:
CharArrayReader(char[] buf) 根據指定的 char 數組建立一個 CharArrayReader
CharArrayReader(char[] buf, int offset, int length)
CharArrayWriter() 建立一個新的 CharArrayWriter ,預設緩沖區大小為 32
CharArrayWriter(int initialSize) 建立一個具有指定初始緩沖區大小的新 CharArrayWriter

注意:設定初始大小不會阻止 CharArrayWriter 存儲比初始大小更多的字元。 如果寫入的字元數超過了初始 char 數組的大小,則會建立一個新的更大的 char 數組,并将所有字元複制到新數組中。

一個使用執行個體如下:

CharArrayWriter writer = new CharArrayWriter();
writer.append('H');
writer.write("ello ".toCharArray());
writer.write("World");
char[] chars = writer.toCharArray();
writer.close();

CharArrayReader reader = new CharArrayReader(chars);
int data = reader.read();
while (data != -1) {
    System.out.print((char) data); // Hello World
    data = reader.read();
}
reader.close();
           

注意:為清楚起見,此處已跳過正确的異常處理。 要了解有關正确異常處理的更多資訊,請轉至 Java IO 異常處理。

如果覺得本文有所幫助,歡迎點【推薦】!文章錯誤之處煩請留言。

轉載說明:轉載後必須在文章開頭明顯地給出作者和原文連結;引用必須注明出處;需要二次修改釋出請聯系作者征得同意。