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

目錄:系統(tǒng)學習 Java IO---- 目錄,概覽

Reader

Reader 類是 Java IO API 中所有 Reader 子類的基類。 Reader 類似于 InputStream ,除了它是基于字符而不是基于字節(jié)的。 換句話說, Reader 用于讀取文本,而 InputStream 用于讀取原始字節(jié)。

Writer

Writer 類是 Java IO API 中所有 Writer 子類的基類。 Writer 就像一個 OutputStream ,除了它是基于字符而不是基于字節(jié)的。 換句話說,Writer 用于寫入文本,而 OutputStream 用于寫入原始字節(jié)。
Writer 通常連接到某些數(shù)據(jù)目標,如文件,字符數(shù)組,網(wǎng)絡(luò)套接字等。

Unicode中的字符

許多應(yīng)用程序使用 UTF(UTF-8或UTF-16)來存儲文本數(shù)據(jù)。 可能需要一個或多個字節(jié)來表示 UTF-8 中的單個字符。 在 UTF-16 中,每個字符需要 2 個字節(jié)來表示。 因此,在讀取或?qū)懭胛谋緮?shù)據(jù)時,數(shù)據(jù)中的單個字節(jié)可能與 UTF 中的一個字符不對應(yīng)。 如果只是通過 InputStream 一次讀取或?qū)懭?UTF-8 數(shù)據(jù)的一個字節(jié),并嘗試將每個字節(jié)轉(zhuǎn)換為字符,可能不會得到正確的文本。

Reader 類能夠?qū)⒆止?jié)解碼為字符。 只需要告訴Reader 要解碼的字符集。 這是在實例化 Reader 時執(zhí)行的(當實例化其中一個子類時)。 通常會直接使用 Reader 子類而不是 Reader。Writer 同理。

讀取

部分方法如下:

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

具體的使用需要參考對應(yīng)的子類。

和 InputStream 類似,如果 read() 方法返回 -1 ,則 Reader 中沒有更多數(shù)據(jù)要讀取,并且可以關(guān)閉它。-1 作為 int 值,而不是 -1 作為 byte 或 char 值。

將字節(jié)流包裝成字符流 InputStreamReader/OutputStreamWrite

InputStreamReade

InputStreamReade 類用于包裝 InputStream ,從而將基于字節(jié)的輸入流轉(zhuǎn)換為基于字符的 Reader 。 換句話說,InputStreamReader 將 InputStream 的字節(jié)解釋為文本而不是數(shù)字數(shù)據(jù),是字節(jié)流通向字符流的橋梁。
為了達到最高效率,可要考慮在 BufferedReader 內(nèi)包裝 InputStreamReader。例如:

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

通常用于從文件(或網(wǎng)絡(luò)連接)中讀取字符,其中字節(jié)表示文本。 例如,一個文本文件,其中字符編碼為 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();

首先創(chuàng)建一個 FileInputStream ,然后將其包裝在 InputStreamReader 中。 其次,該示例通過 InputStreamReader 從文件中讀取所有字符.
注意:為清楚起見,此處已跳過正確的異常處理。 要了解有關(guān)正確異常處理的更多信息,請轉(zhuǎn)至目錄的 Java IO 異常處理。

指定字符集

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

默認字符集可能因為環(huán)境不同而不同,所以建議告訴 InputStreamReader 實例 InputStream 中的字符用什么字符集進行編碼。 這可以在 InputStreamReader 構(gòu)造函數(shù)中指定,可以只提供字符集的名字字符串,在底層會調(diào)用 Charset.forName("UTF-8") 進行轉(zhuǎn)換的。 以下是設(shè)置 InputStreamReader 使用的字符集的示例:

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

同樣建議使用 try with resources 來關(guān)閉流。同樣,只要關(guān)閉最外層的包裝流,里面的流會被系統(tǒng)自動關(guān)閉。

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

OutputStreamWriter

OutputStreamWriter 類用于包裝 OutputStream ,從而將基于字節(jié)的輸出流轉(zhuǎn)換為基于字符的 Writer 。

如果需要將字符寫入文件,OutputStreamWriter 非常有用,例如編碼為 UTF-8 或 UTF-16。 然后可以將字符(char 值)寫入 OutputStreamWriter ,它將正確編碼它們并將編碼的字節(jié)寫入底層的 OutputStream 。
一個簡單的示例如下:

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

注意:為清楚起見,此處已跳過正確的異常處理。 要了解有關(guān)正確異常處理的更多信息,請轉(zhuǎn)至目錄的 Java IO 異常處理。

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

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

參考關(guān)閉 InputStreamReader 或 Java IO 異常處理。

讀寫文件 FileReader/FileWriter

FileReader

FileReader類使得可以將文件的內(nèi)容作為字符流讀取。 它的工作方式與 FileInputStream 非常相似,只是 FileInputStream 讀取字節(jié),而 FileReader 讀取字符。 換句話說,F(xiàn)ileReader 旨在讀取文本。 取決于字符編碼方案,一個字符可以對應(yīng)于一個或多個字節(jié)。

FileReader 假定您要使用運行應(yīng)用程序的計算機的默認字符編碼來解碼文件中的字節(jié)。 這可能并不總是你想要的,但是不能改變它!
如果要指定其他字符解碼方案,請不要使用 FileReader 。 而是在 FileInputStream 上使用 InputStreamReader 。 InputStreamReader 允許您指定在讀取基礎(chǔ)文件中的字節(jié)時使用的字符編碼方案。

FileWriter

FileWriter 類可以將字符寫入文件。 在這方面它的工作原理與 FileOutputStream 非常相似,只是 FileOutputStream 是基于字節(jié)的,而 FileWriter 是基于字符的。 換句話說,F(xiàn)ileWriter 用于寫文本。 一個字符可以對應(yīng)于一個或多個字節(jié),這取決于使用的字符編碼方案。
創(chuàng)建 FileWriter 時,可以決定是否要覆蓋具有相同名稱的任何現(xiàn)有文件,或者只是要追加內(nèi)容到現(xiàn)有文件。 可以通過選擇使用的 FileWriter 構(gòu)造函數(shù)來決定。

  • FileWriter(File file, boolean append): 根據(jù)給定的 File 對象構(gòu)造一個 FileWriter 對象。 示例如下:
Writer fileWriter = new FileWriter("c:\\data\\output.txt", true);  // 追加模式
Writer fileWriter = new FileWriter("c:\\data\\output.txt", false); // 默認情況,直接覆蓋原文件

注意,只要成功 new 了一個 FileWriter 對象,沒有指定是追加模式的話,那不管有沒有調(diào)用 write() 方法,都會清空文件內(nèi)容。

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

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();
    }
}

注意:為清楚起見,此處已跳過正確的異常處理。 要了解有關(guān)正確異常處理的更多信息,請轉(zhuǎn)至Java IO異常處理。

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

PipedReader/PipedWriter

讀寫管道 PipedReader 和 PipedWriter

PipedReader 類使得可以將管道的內(nèi)容作為字符流讀取。 因此它的工作方式與 PipedInputStream 非常相似,只是PipedInputStream 是基于字節(jié)的,而不是基于字符的。 換句話說,PipedReader 旨在讀取文本。PipedWriter 同理。

構(gòu)造器
方法 描述
PipedReader() 創(chuàng)建尚未連接的 PipedReader。
PipedReader(int pipeSize) 創(chuàng)建一個尚未連接的 PipedReader,并對管道緩沖區(qū)使用指定的管道大小。
PipedReader(PipedWriter src) 創(chuàng)建直接連接到傳送 PipedWriter src 的 PipedReader。
PipedReader(PipedWriter src, int pipeSize) 創(chuàng)建一個 PipedReader,使其連接到管道 writer src,并對管道緩沖區(qū)使用指定的管道大小。
PipedWriter() 創(chuàng)建一個尚未連接到傳送 reader 的傳送 writer。
PipedWriter(PipedReader snk) 創(chuàng)建傳送 writer,使其連接到指定的傳送 reader。
讀寫之前,必須先建立連接

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

  1. 通過構(gòu)造器創(chuàng)建,偽代碼如 Piped piped1 = new Piped(piped2);
  2. 調(diào)用其中一個的 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 。 當這兩個字符流連接時,它們形成一個管道。 要了解有關(guān) Java IO 管道的更多信息,請參考 管道流 PipedInputStream 部分。

讀寫字符數(shù)組 CharArrayReader/CharArrayWriter

ByteArrayInputStream/ByteArrayOutputStream 是對字節(jié)數(shù)組處理,CharArrayReader/CharArrayWriter 則是對字符數(shù)組進行處理,其用法是基本一致的,所以這里略微帶過。

CharArrayReader

CharArrayReader 類可以將 char 數(shù)組的內(nèi)容作為字符流讀取。
只需將 char 數(shù)組包裝在 CharArrayReader 中就可以很方便的生成一個 Reader 對象。

CharArrayWriter

CharArrayWriter 類可以通過 Writer 方法(CharArrayWriter是Writer的子類)編寫字符,并將寫入的字符轉(zhuǎn)換為 char 數(shù)組。
在寫入所有字符時,CharArrayWriter 上調(diào)用 toCharArray() 能很方便的生成一個字符數(shù)組。

兩個類的構(gòu)造函數(shù):
方法 描述
CharArrayReader(char[] buf) 根據(jù)指定的 char 數(shù)組創(chuàng)建一個 CharArrayReader
CharArrayReader(char[] buf, int offset, int length) 根據(jù)指定的 char 數(shù)組創(chuàng)建一個 CharArrayReader
CharArrayWriter() 創(chuàng)建一個新的 CharArrayWriter ,默認緩沖區(qū)大小為 32
CharArrayWriter(int initialSize) 創(chuàng)建一個具有指定初始緩沖區(qū)大小的新 CharArrayWriter

注意:設(shè)置初始大小不會阻止 CharArrayWriter 存儲比初始大小更多的字符。 如果寫入的字符數(shù)超過了初始 char 數(shù)組的大小,則會創(chuàng)建一個新的更大的 char 數(shù)組,并將所有字符復(fù)制到新數(shù)組中。

一個使用實例如下:

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();

注意:為清楚起見,此處已跳過正確的異常處理。 要了解有關(guān)正確異常處理的更多信息,請轉(zhuǎn)至 Java IO 異常處理。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容