系統(tǒng)學(xué)習(xí) Java IO (十三)----字符讀寫(xiě) Reader/Writer 及其常用子類(lèi)

目錄:系統(tǒng)學(xué)習(xí) Java IO---- 目錄瘟忱,概覽

Reader

Reader 類(lèi)是 Java IO API 中所有 Reader 子類(lèi)的基類(lèi)勇婴。 Reader 類(lèi)似于 InputStream 盛泡,除了它是基于字符而不是基于字節(jié)的。 換句話說(shuō)弧械, Reader 用于讀取文本粗仓,而 InputStream 用于讀取原始字節(jié)。

Writer

Writer 類(lèi)是 Java IO API 中所有 Writer 子類(lèi)的基類(lèi)奸绷。 Writer 就像一個(gè) OutputStream 梗夸,除了它是基于字符而不是基于字節(jié)的。 換句話說(shuō)号醉,Writer 用于寫(xiě)入文本反症,而 OutputStream 用于寫(xiě)入原始字節(jié)。
Writer 通常連接到某些數(shù)據(jù)目標(biāo)畔派,如文件铅碍,字符數(shù)組,網(wǎng)絡(luò)套接字等线椰。

Unicode中的字符

許多應(yīng)用程序使用 UTF(UTF-8或UTF-16)來(lái)存儲(chǔ)文本數(shù)據(jù)胞谈。 可能需要一個(gè)或多個(gè)字節(jié)來(lái)表示 UTF-8 中的單個(gè)字符。 在 UTF-16 中,每個(gè)字符需要 2 個(gè)字節(jié)來(lái)表示烦绳。 因此卿捎,在讀取或?qū)懭胛谋緮?shù)據(jù)時(shí),數(shù)據(jù)中的單個(gè)字節(jié)可能與 UTF 中的一個(gè)字符不對(duì)應(yīng)爵嗅。 如果只是通過(guò) InputStream 一次讀取或?qū)懭?UTF-8 數(shù)據(jù)的一個(gè)字節(jié)娇澎,并嘗試將每個(gè)字節(jié)轉(zhuǎn)換為字符,可能不會(huì)得到正確的文本睹晒。

Reader 類(lèi)能夠?qū)⒆止?jié)解碼為字符趟庄。 只需要告訴Reader 要解碼的字符集。 這是在實(shí)例化 Reader 時(shí)執(zhí)行的(當(dāng)實(shí)例化其中一個(gè)子類(lèi)時(shí))伪很。 通常會(huì)直接使用 Reader 子類(lèi)而不是 Reader戚啥。Writer 同理。

讀取

部分方法如下:

方法 描述
void mark(int readAheadLimit) 標(biāo)記流中的當(dāng)前位置锉试。
int read() 讀取單個(gè)字符猫十。
int read(char[] cbuf) 將字符讀入數(shù)組。
abstract int read(char[] cbuf, int off, int len) 將字符讀入數(shù)組的某一部分呆盖。
int read(CharBuffer target) 試圖將字符讀入指定的字符緩沖區(qū)拖云。
boolean ready() 判斷是否準(zhǔn)備讀取此流。

具體的使用需要參考對(duì)應(yīng)的子類(lèi)应又。

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

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

InputStreamReade

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

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

通常用于從文件(或網(wǎng)絡(luò)連接)中讀取字符,其中字節(jié)表示文本躁染。 例如鸣哀,一個(gè)文本文件,其中字符編碼為 UTF-8 吞彤。 可以使用InputStreamReader 來(lái)包裝 FileInputStream 以讀取此類(lèi)文件我衬。
一個(gè)示例如下:

 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)建一個(gè) FileInputStream 叹放,然后將其包裝在 InputStreamReader 中。 其次挠羔,該示例通過(guò) InputStreamReader 從文件中讀取所有字符.
注意:為清楚起見(jiàn)井仰,此處已跳過(guò)正確的異常處理。 要了解有關(guān)正確異常處理的更多信息破加,請(qǐng)轉(zhuǎn)至目錄的 Java IO 異常處理俱恶。

指定字符集

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

默認(rèn)字符集可能因?yàn)榄h(huán)境不同而不同锭环,所以建議告訴 InputStreamReader 實(shí)例 InputStream 中的字符用什么字符集進(jìn)行編碼聪全。 這可以在 InputStreamReader 構(gòu)造函數(shù)中指定,可以只提供字符集的名字字符串辅辩,在底層會(huì)調(diào)用 Charset.forName("UTF-8") 進(jìn)行轉(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 來(lái)關(guān)閉流。同樣玫锋,只要關(guān)閉最外層的包裝流蛾茉,里面的流會(huì)被系統(tǒng)自動(dò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 類(lèi)用于包裝 OutputStream 撩鹿,從而將基于字節(jié)的輸出流轉(zhuǎn)換為基于字符的 Writer 臀稚。

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

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

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

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

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

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

讀寫(xiě)文件 FileReader/FileWriter

FileReader

FileReader類(lèi)使得可以將文件的內(nèi)容作為字符流讀取碱茁。 它的工作方式與 FileInputStream 非常相似裸卫,只是 FileInputStream 讀取字節(jié),而 FileReader 讀取字符纽竣。 換句話說(shuō)墓贿,F(xiàn)ileReader 旨在讀取文本茧泪。 取決于字符編碼方案,一個(gè)字符可以對(duì)應(yīng)于一個(gè)或多個(gè)字節(jié)聋袋。

FileReader 假定您要使用運(yùn)行應(yīng)用程序的計(jì)算機(jī)的默認(rèn)字符編碼來(lái)解碼文件中的字節(jié)队伟。 這可能并不總是你想要的,但是不能改變它幽勒!
如果要指定其他字符解碼方案嗜侮,請(qǐng)不要使用 FileReader 。 而是在 FileInputStream 上使用 InputStreamReader 啥容。 InputStreamReader 允許您指定在讀取基礎(chǔ)文件中的字節(jié)時(shí)使用的字符編碼方案锈颗。

FileWriter

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

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

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

下面是一個(gè)讀和寫(xiě)的例子:

public class FileRW {
    public static void main(String[] args) throws IOException {
        // 默認(rèn)是覆蓋模式
        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) {
            // 將會(huì)輸出 string from writer1, append content from writer2
            System.out.print((char) data);
            data = reader.read();
        }
        reader.close();
    }
}

注意:為清楚起見(jiàn)详炬,此處已跳過(guò)正確的異常處理盐类。 要了解有關(guān)正確異常處理的更多信息,請(qǐng)轉(zhuǎn)至Java IO異常處理呛谜。

其他行為和 InputStreamReader 差不多在跳,就不展開(kāi)講了。

PipedReader/PipedWriter

讀寫(xiě)管道 PipedReader 和 PipedWriter

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

構(gòu)造器
方法 描述
PipedReader() 創(chuàng)建尚未連接的 PipedReader韭脊。
PipedReader(int pipeSize) 創(chuàng)建一個(gè)尚未連接的 PipedReader童谒,并對(duì)管道緩沖區(qū)使用指定的管道大小。
PipedReader(PipedWriter src) 創(chuàng)建直接連接到傳送 PipedWriter src 的 PipedReader。
PipedReader(PipedWriter src, int pipeSize) 創(chuàng)建一個(gè) PipedReader,使其連接到管道 writer src紊浩,并對(duì)管道緩沖區(qū)使用指定的管道大小。
PipedWriter() 創(chuàng)建一個(gè)尚未連接到傳送 reader 的傳送 writer琅豆。
PipedWriter(PipedReader snk) 創(chuàng)建傳送 writer,使其連接到指定的傳送 reader篓吁。
讀寫(xiě)之前茫因,必須先建立連接

PipedReader 必須連接到 PipedWriter 才可以讀 ,PipedWriter 也必須始終連接到 PipedReader 才可以寫(xiě)杖剪。就是說(shuō)讀寫(xiě)之前冻押,必須先建立連接,有兩種方式可以建立連接盛嘿。

  1. 通過(guò)構(gòu)造器創(chuàng)建洛巢,偽代碼如 Piped piped1 = new Piped(piped2);
  2. 調(diào)用其中一個(gè)的 connect() 方法,偽代碼如 Piped1.connect(Piped2);

并且通常次兆,PipedReader 和 PipedWriter 由不同的線程使用稿茉。 注意只有一個(gè) PipedReader 可以連接到同一個(gè) PipedWriter ,一個(gè)示例如下:

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

注意:為清楚起見(jiàn)芥炭,這里忽略了正確的 IO 異常處理漓库,并且沒(méi)有使用不同線程,不同線程操作請(qǐng)參考 PipedInputStream 园蝠。

正如在上面的示例中所看到的渺蒿,PipedReader 需要連接到 PipedWriter 。 當(dāng)這兩個(gè)字符流連接時(shí)砰琢,它們形成一個(gè)管道。 要了解有關(guān) Java IO 管道的更多信息良瞧,請(qǐng)參考 管道流 PipedInputStream 部分陪汽。

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

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

CharArrayReader

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

CharArrayWriter

CharArrayWriter 類(lèi)可以通過(guò) Writer 方法(CharArrayWriter是Writer的子類(lèi))編寫(xiě)字符澳骤,并將寫(xiě)入的字符轉(zhuǎn)換為 char 數(shù)組。
在寫(xiě)入所有字符時(shí)澜薄,CharArrayWriter 上調(diào)用 toCharArray() 能很方便的生成一個(gè)字符數(shù)組为肮。

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

注意:設(shè)置初始大小不會(huì)阻止 CharArrayWriter 存儲(chǔ)比初始大小更多的字符肤京。 如果寫(xiě)入的字符數(shù)超過(guò)了初始 char 數(shù)組的大小颊艳,則會(huì)創(chuàng)建一個(gè)新的更大的 char 數(shù)組,并將所有字符復(fù)制到新數(shù)組中忘分。

一個(gè)使用實(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();

注意:為清楚起見(jiàn)棋枕,此處已跳過(guò)正確的異常處理。 要了解有關(guān)正確異常處理的更多信息妒峦,請(qǐng)轉(zhuǎn)至 Java IO 異常處理重斑。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市肯骇,隨后出現(xiàn)的幾起案子窥浪,更是在濱河造成了極大的恐慌,老刑警劉巖累盗,帶你破解...
    沈念sama閱讀 212,599評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件寒矿,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡若债,警方通過(guò)查閱死者的電腦和手機(jī)符相,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,629評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)蠢琳,“玉大人啊终,你說(shuō)我怎么就攤上這事“列耄” “怎么了蓝牲?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,084評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)泰讽。 經(jīng)常有香客問(wèn)我例衍,道長(zhǎng),這世上最難降的妖魔是什么已卸? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,708評(píng)論 1 284
  • 正文 為了忘掉前任佛玄,我火速辦了婚禮,結(jié)果婚禮上累澡,老公的妹妹穿的比我還像新娘梦抢。我一直安慰自己,他們只是感情好愧哟,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,813評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布奥吩。 她就那樣靜靜地躺著哼蛆,像睡著了一般。 火紅的嫁衣襯著肌膚如雪霞赫。 梳的紋絲不亂的頭發(fā)上腮介,一...
    開(kāi)封第一講書(shū)人閱讀 50,021評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音绩脆,去河邊找鬼萤厅。 笑死,一個(gè)胖子當(dāng)著我的面吹牛靴迫,可吹牛的內(nèi)容都是我干的惕味。 我是一名探鬼主播,決...
    沈念sama閱讀 39,120評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼玉锌,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼名挥!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起主守,我...
    開(kāi)封第一講書(shū)人閱讀 37,866評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤禀倔,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后参淫,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體救湖,經(jīng)...
    沈念sama閱讀 44,308評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,633評(píng)論 2 327
  • 正文 我和宋清朗相戀三年涎才,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了鞋既。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,768評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡耍铜,死狀恐怖邑闺,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情棕兼,我是刑警寧澤陡舅,帶...
    沈念sama閱讀 34,461評(píng)論 4 333
  • 正文 年R本政府宣布,位于F島的核電站伴挚,受9級(jí)特大地震影響靶衍,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜茎芋,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,094評(píng)論 3 317
  • 文/蒙蒙 一颅眶、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧败徊,春花似錦帚呼、人聲如沸掏缎。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,850評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至沪哺,卻和暖如春沈自,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背辜妓。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,082評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工枯途, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人籍滴。 一個(gè)月前我還...
    沈念sama閱讀 46,571評(píng)論 2 362
  • 正文 我出身青樓酪夷,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親孽惰。 傳聞我的和親對(duì)象是個(gè)殘疾皇子晚岭,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,666評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容