詳解Java I/O流中的字符流有哪些
Java中的字符流,可以分為字符輸入流(Reader)和字符輸出流(Writer),輸入流用于讀取數(shù)據(jù),輸出流用于寫(xiě)入數(shù)據(jù),接下來(lái)就讓我們來(lái)逐個(gè)了解吧。
一. Reader字符輸入流
1. 簡(jiǎn)介
Reader是I/O庫(kù)中用于讀取字符流的抽象類(lèi),它提供了一組方法用于讀取字符流中的字符,并支持不同的字符編碼。
實(shí)際上,除了特殊的CharArrayReader和StringReader,普通的Reader本質(zhì)上就是一個(gè)帶有編碼轉(zhuǎn)換器的InputStream,它可以把byte轉(zhuǎn)換為char。也就是說(shuō),普通的Reader就是基于InputStream構(gòu)造出來(lái)的,比如在FileReader的源碼內(nèi)部就有一個(gè)FileInputStream對(duì)象。而我們也完全可以把一個(gè)InputStream轉(zhuǎn)換為Reader,比如:
// InputStream對(duì)象 InputStream is = new FileInputStream("a.txt"); // InputStream轉(zhuǎn)為Reader Reader reader = new InputStreamReader(is, "UTF-8");
在這里,InputStreamReader就是一種用于將字節(jié)流轉(zhuǎn)換為字符流的轉(zhuǎn)換流,使用轉(zhuǎn)換流可以在一定程度上避免亂碼,還可以指定輸入輸出所使用的字符集。其中,InputStreamReader用于將字節(jié)輸入流轉(zhuǎn)換為字符輸入流,OutputStreamWriter用于將字節(jié)輸出流轉(zhuǎn)換為字符輸出流。
Reader和InputStream之間有關(guān)系,當(dāng)然也有很多區(qū)別,比如:
- InputStream是一個(gè)字節(jié)流,即以byte為單位讀取,讀取的字節(jié)范圍是(-1,0~255),數(shù)據(jù)會(huì)讀到字節(jié)數(shù)組中;
- 而Reader是一個(gè)字符流,要以char為單位讀取,讀取的字符范圍是(-1,0~65535),數(shù)據(jù)會(huì)讀到字符數(shù)組中。
2. 常用子類(lèi)
因?yàn)镽eader是抽象類(lèi),所以我們不能直接對(duì)其進(jìn)行實(shí)例化,如果我們想使用字符輸入流,需要使用Reader的某個(gè)具體子類(lèi)來(lái)實(shí)例化對(duì)象。Reader的常用子類(lèi)有如下幾個(gè):
- InputStreamReader類(lèi):將字節(jié)輸入流轉(zhuǎn)換為字符輸入流,可以指定字符編碼;
- BufferedReader類(lèi):為其他字符輸入流提供讀緩沖區(qū);
- CharArrayReader類(lèi):將字符數(shù)組轉(zhuǎn)換為字符輸入流,從中讀取字符;
- StringReader類(lèi):將字符串轉(zhuǎn)換為字符輸入流,從中讀取字符;
- FileReader類(lèi):用于讀取文本文件,并支持不同的字符編碼;
- PipedReader類(lèi):連接到一個(gè)PipedWriter。
3. 常用方法
Reader字符流中的常用方法,其實(shí)與InputStream中的常用方法基本一致,比如:
方法名及返回值類(lèi)型 | 說(shuō)明 |
---|---|
int read() | 從輸入流中讀取一個(gè)字符,并把它轉(zhuǎn)換為 0~65535 之間的整數(shù)。如果返回 -1, 則表示已經(jīng)到了輸入流的末尾。該方法不常用。 |
int read(char[] cbuf) | 從輸入流中讀取若干個(gè)字符,并把它們保存到cbuf參數(shù)指定的字符數(shù)組中。最終返回讀取的字符數(shù),如果返回 -1,則表示已經(jīng)到了輸入流的末尾。 |
int read(char[] cbuf,int off,int len) | 從輸入流中讀取若干個(gè)字符,并把它們保存到cbuf參數(shù)指定的字符數(shù)組中。off表示字符數(shù)組中開(kāi)始保存數(shù)據(jù)的起始下標(biāo),len表示讀取的字符數(shù)。最后返回實(shí)際讀取的字符數(shù),如果返回 -1,則表示已經(jīng)到了輸入流的末尾。 |
4. 實(shí)現(xiàn)步驟
如果我們想使用Reader讀取字符流,可以遵循以下基本步驟:
創(chuàng)建一個(gè)Reader對(duì)象,比如InputStreamReader、FileReader、StringReader等;
調(diào)用Reader的read()方法來(lái)讀取字符流,該方法會(huì)返回一個(gè)整數(shù),表示讀取的字符數(shù)。如果已到達(dá)流的末尾,則read()方法返回-1;
處理讀取的字符,可以將它們存儲(chǔ)在數(shù)組或字符串中;
循環(huán)調(diào)用read()方法,直到讀取完整個(gè)字符流。
5. 代碼案例
為了讀取方便,Java給我們提供了用來(lái)讀取字符文件的便捷類(lèi)——FileReader,所以這里利用FileReader讀取了一個(gè)文本文件。
import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; public class Demo08 { public static void main(String[] args) throws FileNotFoundException, IOException { // try(resource)的寫(xiě)法 //1. 創(chuàng)建Reader對(duì)象 try (FileReader reader = new FileReader("F:/a.txt")) { //設(shè)置編碼為UTF-8 //Reader reader = new FileReader("F:/a.txt", StandardCharsets.UTF_8); //2. 讀取文件 char[] cbuf = new char[1024]; int len; while((len = reader.read(cbuf)) != -1) { //3.處理字符 String str = new String(cbuf, 0, len); System.out.println(str); } } catch (IOException e) { e.printStackTrace(); } //4.自動(dòng)資源釋放 } }
在上面的案例中,read()方法可以一次讀取一個(gè)字符數(shù)組,并返回實(shí)際讀入的字符個(gè)數(shù),最大不會(huì)超過(guò)char[]數(shù)組的長(zhǎng)度,如果讀到流的末尾就返回-1。所以利用這個(gè)方法,我們可以先設(shè)置一個(gè)緩沖區(qū),然后每次盡可能地填充緩沖區(qū)。
大家可以發(fā)現(xiàn),其實(shí)Reader字符流的使用,與InputStream字節(jié)流的使用基本類(lèi)似。
另外我們?cè)跇?gòu)造FileReader對(duì)象時(shí),默認(rèn)的字符編碼及字節(jié)緩沖區(qū)大小都是由系統(tǒng)設(shè)定好的,如果我們想自己指定這些值,可以在FilelnputStream上套接一個(gè)InputStreamReader。在上面的代碼中,如果我們讀取的是純ASCII編碼的文本文件,輸出的內(nèi)容不會(huì)產(chǎn)生亂碼。但如果文件中包含中文,就會(huì)出現(xiàn)亂碼,這就是因?yàn)镕ileReader采用的是操作系統(tǒng)默認(rèn)的編碼,在Windows中默認(rèn)的是GBK,而打開(kāi)UTF-8編碼的文本文件可能就會(huì)出現(xiàn)亂碼。所以為了避免讀取中文時(shí)產(chǎn)生亂碼,我們可以在創(chuàng)建FileReader時(shí)指定編碼:
Reader reader = new FileReader("a.txt", StandardCharsets.UTF_8);
二. Writer字符輸出流
1. 簡(jiǎn)介
Writer是I/O庫(kù)中用于寫(xiě)入字符流的抽象類(lèi),它提供了一組方法用于將字符寫(xiě)入到字符流中,并支持不同的字符編碼。除了CharArrayWriter和StringWriter之外,普通的Writer都是基于OutputStream構(gòu)造的。所以本質(zhì)上Writer是一個(gè)帶有編碼轉(zhuǎn)換器的OutputStream,可以把char轉(zhuǎn)換為byte并輸出,即接收char,然后在內(nèi)部會(huì)自動(dòng)轉(zhuǎn)換成一個(gè)或多個(gè)byte,并寫(xiě)入到OutputStream中。比如我們可以利用OutputStreamWriter,將任意的OutputStream轉(zhuǎn)換為Writer,所以OutputStreamWriter是一種用于將字節(jié)輸出流轉(zhuǎn)換為字符輸出流的轉(zhuǎn)換流:
//創(chuàng)建FileOutputStream對(duì)象 FileOutputStream fos=new FileOutputStream("readme.txt"); //將FileOutputStream轉(zhuǎn)為Writer Writer writer = new OutputStreamWriter(fos, "UTF-8")
同樣的,Writer和OutputStream也具有一些區(qū)別:
- OutputStream是一個(gè)字節(jié)流,即以byte為單位讀取,讀取的字節(jié)范圍是0~255;
- Writer是一個(gè)字符流,要以char為單位讀取,讀取的字符范圍是0~65535。
2. 常用子類(lèi)
Writer是所有字符輸出流的父類(lèi),常用的Writer子類(lèi)有如下這些:
- OutputStreamReader類(lèi):將字節(jié)輸出流轉(zhuǎn)換為字符輸出流,可以指定字符編碼;
- BufferedWriter類(lèi):為其他字符輸出流提供寫(xiě)緩沖區(qū);
- CharArrayWriter類(lèi):向內(nèi)存緩沖區(qū)的字符數(shù)組寫(xiě)數(shù)據(jù);
- StringWriter類(lèi):向內(nèi)存緩沖區(qū)的字符串(StringBuffer)寫(xiě)入數(shù)據(jù);
- FileReader類(lèi):用于寫(xiě)入文本文件,并支持不同的字符編碼;
- PipedWriter類(lèi):連接到一個(gè)PipedReader對(duì)象。
3. 常用方法
同樣的,我們也來(lái)看看Writer類(lèi)中的常用方法:
方法名及返回值類(lèi)型 | 說(shuō)明 |
---|---|
void write(int c) | 向輸出流中寫(xiě)入一個(gè)字符。 |
void write(char[] cbuf) | 把cbuf參數(shù)指定的字符數(shù)組中的所有字符,寫(xiě)到輸出流中。 |
void write(char[] cbuf,int off,int len) | 把cbuf參數(shù)指定的字符數(shù)組中的若干字符寫(xiě)到輸出流中。off 表示字符數(shù)組的起始下標(biāo),len表示元素的個(gè)數(shù)。 |
void write(String str) | 向輸出流中寫(xiě)入一個(gè)字符串。 |
void write(String str, int off,int len) | 向輸出流中寫(xiě)入一個(gè)字符串的部分字符。off表示字符串中的起始偏移量,len表示字符個(gè)數(shù)。 |
append(char c) | 將參數(shù)c指定的字符添加到輸出流中。 |
append(charSequence esq) | 將參數(shù)esq指定的字符序列添加到輸出流中。 |
append(charSequence esq,int start,int end) | 將參數(shù)esq指定的字符序列的子序列添加到輸出流中。start表示子序列中的第一個(gè)字符索引,end表示子序列中最后一個(gè)字符后面的字符索引。即子序列的內(nèi)容包含start索引處的字符,但不包括end索引處的字符。 |
大家要注意:Writer類(lèi)中所有的方法在出錯(cuò)時(shí)都會(huì)引發(fā)IOException異常,如果我們關(guān)閉一個(gè)流后再對(duì)其進(jìn)行任何操作,都會(huì)產(chǎn)生錯(cuò)誤。
4. 代碼案例
我們?cè)O(shè)計(jì)一個(gè)FileWriter的使用案例,代碼如下:
import java.io.FileNotFoundException; import java.io.FileWriter; import java.io.IOException; import java.util.Scanner; public class Demo09 { public static void main(String[] args) throws FileNotFoundException, IOException { // try(resource)的寫(xiě)法 // 1. 創(chuàng)建Writer對(duì)象 try (FileWriter writer = new FileWriter("F:/b.txt")) { // 利用Scanner進(jìn)行內(nèi)容的輸入 Scanner input = new Scanner(System.in); // 2. 寫(xiě)入文件 for (int i = 0; i < 5; i++) { System.out.println("請(qǐng)輸入第" + (i + 1) + "行內(nèi)容:"); // 讀取輸入的名稱(chēng) String content = input.next(); // 循環(huán)寫(xiě)入到文件中 writer.write(content + "\r\n"); //writer.append(“追加新內(nèi)容...”); } System.out.println("錄入完畢"); input.close(); } catch (IOException e) { e.printStackTrace(); } // 3.自動(dòng)資源釋放 } }
FileWriter是向文件中寫(xiě)入字符流的Writer,它的使用和FileReader類(lèi)似。這里的FileWriter對(duì)象直接關(guān)聯(lián)了一個(gè)文件,然后我們可以調(diào)用write()或append()方法進(jìn)行內(nèi)容的新增和追加了。我們?cè)趧?chuàng)建FileWriter對(duì)象時(shí),默認(rèn)的字符編碼和字節(jié)緩沖區(qū)的大小都是由系統(tǒng)設(shè)定的。如果我們想要自己指定這些值,可以在FileOutputStream上套接一個(gè)OutputStreamWriter對(duì)象。
在創(chuàng)建FileWriter類(lèi)對(duì)象時(shí),如果關(guān)聯(lián)的文件不存在,則會(huì)自動(dòng)生成一個(gè)新的文件。在創(chuàng)建文件之前,F(xiàn)ileWriter會(huì)在創(chuàng)建對(duì)象時(shí)打開(kāi)該文件作為輸出目的地,但如果試圖打開(kāi)的是一個(gè)只讀文件,會(huì)引發(fā)IOException異常。
三. 字符緩沖流
1. 簡(jiǎn)介
與字節(jié)緩沖流類(lèi)似,我們?cè)谶M(jìn)行大文件讀寫(xiě)操作時(shí),也可以使用字符緩沖流來(lái)減少訪(fǎng)問(wèn)磁盤(pán)的次數(shù),提高IO訪(fǎng)問(wèn)效率。Java中的字符緩沖流包括BufferedReader和BufferedWriter兩大類(lèi),分別負(fù)責(zé)文件的讀取和寫(xiě)入。
2. 常用子類(lèi)
Java中的字符緩沖流可以分為緩沖的字符輸入流BufferedReader和緩沖的字符輸出流BufferedWriter。
- BufferedReader:繼承自InputStreamReader 類(lèi), 用于讀取二進(jìn)制數(shù)據(jù),并將數(shù)據(jù)存儲(chǔ)在內(nèi)部緩沖區(qū)中;
- BufferedWriter:繼承自OutputStreamWriter類(lèi),用于寫(xiě)入二進(jìn)制數(shù)據(jù),并將數(shù)據(jù)存儲(chǔ)在內(nèi)部緩沖區(qū)中。
3. BufferedReader的用法
BufferedReader是一個(gè)帶有緩沖區(qū)的輸入流,主要用于輔助其他的字符輸入流。BufferedReader可以先將一批數(shù)據(jù)讀到內(nèi)存緩沖區(qū),然后接下來(lái)的讀操作就可以直接從該緩沖區(qū)中獲取數(shù)據(jù),并進(jìn)行字符編碼轉(zhuǎn)換,這樣就可以提高數(shù)據(jù)的讀取效率。
另外BufferedReader還提供了一個(gè)readLine()方法,該方法會(huì)返回包含所讀內(nèi)容的字符串,但該字符串中不包含任何終止符,如果已到達(dá)了流的末尾,就返回null。readLine()方法表示每次讀取一行的文本內(nèi)容,當(dāng)遇到換行(\n)、回車(chē)(\r)或回車(chē)后直接跟著換行標(biāo)記符
,即認(rèn)為某行終止。接下來(lái)我們就來(lái)看看BufferedReader是怎么使用的吧。
import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; public class Demo11 { public static void main(String[] args) throws FileNotFoundException, IOException { // try(resource)的寫(xiě)法 // 1. 創(chuàng)建Reader對(duì)象 try(BufferedReader reader=new BufferedReader(new FileReader("F:\a.txt"))) { String strLine = ""; while ((strLine = reader.readLine()) != null) { //循環(huán)讀取每行數(shù)據(jù) System.out.println(strLine); } } catch (FileNotFoundException e1) { e1.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } // 3.自動(dòng)資源釋放 } }
4. BufferedWriter的用法
BufferedWriter則是一個(gè)帶有緩沖區(qū)的輸出流,主要用于輔助其他的字符輸出流。BufferedWriter同樣帶有緩沖區(qū),可以先將一批數(shù)據(jù)寫(xiě)入到緩沖區(qū),當(dāng)緩沖區(qū)滿(mǎn)了以后,再將緩沖區(qū)里的數(shù)據(jù)一次性寫(xiě)到字符輸出流,這樣也提高了數(shù)據(jù)的寫(xiě)入效率。
BufferedWriter類(lèi)中提供了一個(gè)新的方法newLine(),該方法用于寫(xiě)入一個(gè)行分隔符。行分隔符字符串由系統(tǒng)屬性 line.separator定義,且不一定是單個(gè)新行(\n)符。BufferedWriter類(lèi)的用法如下:
import java.io.BufferedWriter; import java.io.FileNotFoundException; import java.io.FileWriter; import java.io.IOException; public class Demo12 { public static void main(String[] args) throws FileNotFoundException, IOException { // try(resource)的寫(xiě)法 // 1. 創(chuàng)建Writer對(duì)象 try (BufferedWriter writer = new BufferedWriter(new FileWriter("F:/c.txt"))) { //2.寫(xiě)入內(nèi)容 writer.write("Hello, world!"); //3.換行 writer.newLine(); writer.write("Welcome to learn Java!"); // writer.close(); } catch (IOException e) { e.printStackTrace(); } } }
在上面的代碼中,我們使用了BufferedWriter類(lèi)來(lái)寫(xiě)入字符流,使用newLine()方法添加一個(gè)新行,在寫(xiě)入完成后可以關(guān)閉寫(xiě)入器對(duì)象。
5. 綜合案例
在學(xué)習(xí)了上面的字符輸入流、字符輸出流、轉(zhuǎn)換流以及緩沖流等內(nèi)容之后,最后給大家設(shè)計(jì)一個(gè)綜合案例,來(lái)把這幾個(gè)知識(shí)點(diǎn)糅合在一起。
import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; public class Demo13 { public static void main(String[] args) throws FileNotFoundException, IOException { try { //將System.in對(duì)象轉(zhuǎn)換成InputStreamReader對(duì)象 InputStreamReader reader = new InputStreamReader(System.in); // 將Reader對(duì)象包裝成BufferedReader BufferedReader br = new BufferedReader(reader); String line = null; //利用循環(huán)方式逐行讀取 while ((line = br.readLine()) != null) { // 如果讀取到“exit”,則程序退出 if (line.equals("exit")) { //退出程序 System.exit(1); } // 打印出讀取到的內(nèi)容 System.out.println("輸入的內(nèi)容為:" + line); } } catch (IOException e) { e.printStackTrace(); } } }
在上面的案例中,我們使用了System.in獲取到鍵盤(pán)的輸入信息,該信息其實(shí)是InputStream的實(shí)例。但該實(shí)例使用不太方便,我們使用 InputStreamReader 將其轉(zhuǎn)換成字符輸入流,因?yàn)镽eader讀取輸入內(nèi)容時(shí)依然不太方便,我們繼續(xù)把Reader包裝成BufferedReader。因?yàn)锽ufferReader具有readLine()方法,可以非常方便地一次讀入一行內(nèi)容。而且BufferReader具有緩沖功能,一次讀取一行文本,以換行符為標(biāo)志,如果它沒(méi)有讀到換行符,則程序堵塞,等到讀到換行符為止。運(yùn)行該程序后,在控制臺(tái)執(zhí)行輸入時(shí),只要按下回車(chē)鍵,程序就會(huì)打印出剛剛輸入的內(nèi)容。
四. 結(jié)語(yǔ)
到此這篇關(guān)于詳解Java I/O流中的字符流有哪些的文章就介紹到這了,更多相關(guān)Java字符流內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot配置主從數(shù)據(jù)庫(kù)實(shí)現(xiàn)讀寫(xiě)分離
現(xiàn)在的 Web 應(yīng)用大都是讀多寫(xiě)少,本文主要介紹了SpringBoot配置主從數(shù)據(jù)庫(kù)實(shí)現(xiàn)讀寫(xiě)分離,具有一定的參考價(jià)值,感興趣的可以了解一下2023-11-11編寫(xiě)Java代碼制造一個(gè)內(nèi)存溢出的情況
這篇文章主要介紹了編寫(xiě)Java代碼制造一個(gè)內(nèi)存溢出的情況,或許這種有意制造能夠更好地幫助理解Java中的內(nèi)存溢出情況XD 需要的朋友可以參考下2015-07-07使用Apache Spark進(jìn)行Java數(shù)據(jù)分析的步驟詳解
今天我們將探討如何使用Apache Spark進(jìn)行Java數(shù)據(jù)分析,Apache Spark是一個(gè)強(qiáng)大的大數(shù)據(jù)處理引擎,它支持批處理和流處理,特別適合處理大規(guī)模數(shù)據(jù)集,在Java中使用Spark,我們可以利用其強(qiáng)大的數(shù)據(jù)處理能力來(lái)進(jìn)行各種數(shù)據(jù)分析任務(wù),需要的朋友可以參考下2024-07-07Java 數(shù)據(jù)結(jié)構(gòu)算法Collection接口迭代器示例詳解
這篇文章主要為大家介紹了Java 數(shù)據(jù)結(jié)構(gòu)算法Collection接口迭代器示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09Java中枚舉的實(shí)現(xiàn)與應(yīng)用詳解
這篇文章主要介紹了Java中枚舉的實(shí)現(xiàn)與應(yīng)用詳解,EnumTest中還有一個(gè)VALUES數(shù)組,里面存儲(chǔ)著所有的枚舉實(shí)例,調(diào)用values方法時(shí)返回VALUES數(shù)組的clone,需要的朋友可以參考下2023-12-12Java應(yīng)用程序的CPU使用率飆升原因詳細(xì)分析
這篇文章主要介紹了Java應(yīng)用程序的CPU使用率飆升原因詳細(xì)分析,在 Java 中,我們使用 JVM 進(jìn)行線(xiàn)程調(diào)度,所以一般來(lái)說(shuō),線(xiàn)程的調(diào)度有兩種模式:分時(shí)調(diào)度和搶占式調(diào)度,線(xiàn)程和進(jìn)程在阻塞或者等待時(shí),都不會(huì)使用 CPU 資源,需要的朋友可以參考下2024-01-01