您好,登錄后才能下訂單哦!
這期內(nèi)容當(dāng)中小編將會(huì)給大家?guī)碛嘘P(guān)Java中怎么讀寫字符流文件,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
基類 Reader/Writer
在正式學(xué)習(xí)字符流基類之前,我們需要知道 Java 中是如何表示一個(gè)字符的。
首先,Java 中的默認(rèn)字符編碼為:UTF-8,而我們知道 UTF-8 編碼的字符使用 1 到 4 個(gè)字節(jié)進(jìn)行存儲(chǔ),越常用的字符使用越少的字節(jié)數(shù)。
而 char 類型被定義為兩個(gè)字節(jié)大小,也就是說,對(duì)于通常的字符來說,一個(gè) char 即可存儲(chǔ)一個(gè)字符,但對(duì)于一些增補(bǔ)字符集來說,往往會(huì)使用兩個(gè) char 來表示一個(gè)字符。
Reader 作為讀字符流的基類,它提供了最基本的字符讀取操作,我們一起看看。
先看看它的構(gòu)造器:
protected Object lock; protected Reader() { this.lock = this; } protected Reader(Object lock) { if (lock == null) { throw new NullPointerException(); } this.lock = lock; }
Reader 是一個(gè)抽象類,所以毋庸置疑的是,這些構(gòu)造器是給子類調(diào)用的,用于初始化 lock 鎖對(duì)象,這一點(diǎn)我們后續(xù)會(huì)詳細(xì)解釋。
public int read() throws IOException { char cb[] = new char[1]; if (read(cb, 0, 1) == -1) return -1; else return cb[0]; } public int read(char cbuf[]) throws IOException { return read(cbuf, 0, cbuf.length); } abstract public int read(char cbuf[], int off, int len)
基本的讀字符操作都在這了,第一個(gè)方法用于讀取一個(gè)字符出來,如果已經(jīng)讀到了文件末尾,將返回 -1,同樣的以 int 作為返回值類型接收,為什么不用 char?原因是一樣的,都是由于 -1 這個(gè)值的解釋不確定性。
第二個(gè)方法和第三個(gè)方法是類似的,從文件中讀取指定長度的字符放置到目標(biāo)數(shù)組當(dāng)中。第三個(gè)方法是抽象方法,需要子類自行實(shí)現(xiàn),而第二個(gè)方法卻又是基于它的。
還有一些方法也是類似的:
public long skip(long n):跳過 n 個(gè)字符
public boolean ready():下一個(gè)字符是否可讀
public boolean markSupported():見 reset 方法
public void mark(int readAheadLimit):見 reset 方法
public void reset():用于實(shí)現(xiàn)重復(fù)讀操作
abstract public void close():關(guān)閉流
這些個(gè)方法其實(shí)都見名知意,并且和我們的 InputStream 大體上都差不多,都沒有什么核心的實(shí)現(xiàn),這里不再贅述,你大致知道它內(nèi)部有些個(gè)什么東西即可。
Writer 是寫的字符流,它用于將一個(gè)或多個(gè)字符寫入到文件中,當(dāng)然具體的 write 方法依然是一個(gè)抽象的方法,待子類來實(shí)現(xiàn),所以我們這里亦不再贅述了。
適配器 InpustStramReader/OutputStreamWriter
適配器字符流繼承自基類 Reader 或 Writer,它們算是字符流體系中非常重要的成員了。主要的作用就是,將一個(gè)字節(jié)流轉(zhuǎn)換成一個(gè)字符流,我們先以讀適配器為例。
首先就是它最核心的成員:
private final StreamDecoder sd;
StreamDecoder 是一個(gè)解碼器,用于將字節(jié)的各種操作轉(zhuǎn)換成字符的相應(yīng)操作,關(guān)于它我們會(huì)在后續(xù)的介紹中不間斷的提到它,這里不做統(tǒng)一的解釋。
然后就是構(gòu)造器:
public InputStreamReader(InputStream in) { super(in); try { sd = StreamDecoder.forInputStreamReader(in, this, (String)null); } catch (UnsupportedEncodingException e) { throw new Error(e); } } public InputStreamReader(InputStream in, String charsetName) throws UnsupportedEncodingException { super(in); if (charsetName == null) throw new NullPointerException("charsetName"); sd = StreamDecoder.forInputStreamReader(in, this, charsetName); }
這兩個(gè)構(gòu)造器的目的都是為了初始化這個(gè)解碼器,都調(diào)用的方法 forInputStreamReader,只是參數(shù)不同而已。我們不妨看看這個(gè)方法的實(shí)現(xiàn):
這是一個(gè)典型的靜態(tài)工廠模式,三個(gè)參數(shù),var0 和 var1 沒什么好說的,分別代表的是字節(jié)流實(shí)例和適配器實(shí)例。
而參數(shù) var2 其實(shí)代表的是一種字符編碼的名稱,如果為 null,那么將使用系統(tǒng)默認(rèn)的字符編碼:UTF-8 。
最終我們能夠得到一個(gè)解碼器實(shí)例。
接著介紹的所有方法幾乎都是依賴的這個(gè)解碼器而實(shí)現(xiàn)的。
public String getEncoding() { return sd.getEncoding(); } public int read() throws IOException { return sd.read(); } public int read(char cbuf[], int offset, int length){ return sd.read(cbuf, offset, length); } public void close() throws IOException { sd.close(); }
解碼器中相關(guān)的方法的實(shí)現(xiàn)代碼還是相對(duì)復(fù)雜的,這里我們不做深入的研究,但大體上的實(shí)現(xiàn)思路就是:「字節(jié)流讀取 + 解碼」的過程。
當(dāng)然了,OutputStreamWriter 中必然也存在一個(gè)相反的 StreamEncoder 實(shí)例用于編碼字符。
除了這一點(diǎn)外,其余的操作并沒有什么不同,或是通過字符數(shù)組向文件中寫入,或是通過字符串向文件中寫入,又或是通過 int 的低 16 位向文件中寫入。
文件字符流 FileReader/Writer
文件的字符流可以說非常簡單了,除了構(gòu)造器,就不存在任何其他方法了,完全依賴文件字節(jié)流。
我們以 FileReader 為例,
FileReader 繼承自 InputStreamReader,有且僅有以下三個(gè)構(gòu)造器: public FileReader(String fileName) throws FileNotFoundException { super(new FileInputStream(fileName)); } public FileReader(File file) throws FileNotFoundException { super(new FileInputStream(file)); } public FileReader(FileDescriptor fd) { super(new FileInputStream(fd)); }
理論上來說,所有的字符流都應(yīng)當(dāng)以我們的適配器為基類,因?yàn)橹挥兴峁┝俗址阶止?jié)之間的轉(zhuǎn)換,無論你是寫或是讀都離不開它。
而我們的 FileReader 并沒有擴(kuò)展任何一個(gè)自己的方法,父類 InputStreamReader 中預(yù)實(shí)現(xiàn)的字符操作方法對(duì)他來說已經(jīng)足夠,只需要傳入一個(gè)對(duì)應(yīng)的字節(jié)流實(shí)例即可。
FileWriter 也是一樣的,這里不再贅述了。
字符數(shù)組流 CharArrayReader/Writer
字符數(shù)組和字節(jié)數(shù)組流是類似的,都是用于解決那種不確定文件大小,而需要讀取其中大量內(nèi)容的情況。
由于它們內(nèi)部提供動(dòng)態(tài)擴(kuò)容機(jī)制,所以既可以完全容納目標(biāo)文件,也可以控制數(shù)組大小,不至于分配過大內(nèi)存而浪費(fèi)了大量內(nèi)存空間。
先以 CharArrayReader 為例
protected char buf[]; public CharArrayReader(char buf[]) { this.buf = buf; this.pos = 0; this.count = buf.length; } public CharArrayReader(char buf[], int offset, int length){ //.... }
構(gòu)造器核心任務(wù)就是初始化一個(gè)字符數(shù)組到內(nèi)部的 buf 屬性中,以后所有對(duì)該字符數(shù)組流實(shí)例的讀操作都基于 buf 這個(gè)字符數(shù)組。
關(guān)于 CharArrayReader 的其他方法以及 CharArrayWriter,這里不再贅述了,和上篇的字節(jié)數(shù)組流基本類似。
除此之外,這里還涉及一個(gè) StringReader 和 StringWriter,其實(shí)本質(zhì)上和字符數(shù)組流是一樣的,畢竟 String 的本質(zhì)就是 char 數(shù)組。
緩沖數(shù)組流 BufferedReader/Writer
同樣的,BufferedReader/Writer 作為一種緩沖流,也是裝飾者流,用于提供緩沖功能。大體上類似于我們的字節(jié)緩沖流,這里我們簡單介紹下。
private Reader in; private char cb[]; private static int defaultCharBufferSize = 8192; public BufferedReader(Reader in, int sz){..} public BufferedReader(Reader in) { this(in, defaultCharBufferSize); }
cb 是一個(gè)字符數(shù)組,用于緩存從文件流中讀取出來的部分字符,你可以在構(gòu)造器中初始化這個(gè)數(shù)組的長度,否則將使用默認(rèn)值 8192 。
public int read() throws IOException {..} public int read(char cbuf[], int off, int len){...}
關(guān)于 read,它依賴成員屬性 in 的讀方法,而 in 作為一個(gè) Reader 類型,內(nèi)部往往又依賴的某個(gè) InputStream 實(shí)例的讀方法。
所以說,幾乎所有的字符流都離不開某個(gè)字節(jié)流實(shí)例。
關(guān)于 BufferedWriter,這里也不再贅述了,大體上都是類似的,只不過一個(gè)是讀一個(gè)是寫而已,都圍繞著內(nèi)部的字符數(shù)組進(jìn)行。
標(biāo)準(zhǔn)打印輸出流
打印輸出流主要有兩種,PrintStream 和 PrintWriter,前者是字節(jié)流,后者是字符流。
這兩個(gè)流算是對(duì)各自類別下的流做了一個(gè)集成,內(nèi)部封裝有豐富的方法,但實(shí)現(xiàn)也稍顯復(fù)雜,我們先來看這個(gè) PrintStream 字節(jié)流:
主要的構(gòu)造器有這么幾個(gè):
public PrintStream(OutputStream out)
public PrintStream(OutputStream out, boolean autoFlush)
public PrintStream(OutputStream out, boolean autoFlush, String encoding)
public PrintStream(String fileName)
顯然,簡單的構(gòu)造器會(huì)依賴復(fù)雜的構(gòu)造器,這已經(jīng)算是 jdk 設(shè)計(jì)「老套路」了。區(qū)別于其他字節(jié)流的一點(diǎn)是,PrintStream 提供了一個(gè)標(biāo)志 autoFlush,用于指定是否自動(dòng)刷新緩存。
接著就是 PrintStream 的寫方法:
public void write(int b)
public void write(byte buf[], int off, int len)
除此之外,PrintStream 還封裝了大量的 print 的方法,寫入不同類型的內(nèi)容到文件中,例如:
public void print(boolean b)
public void print(char c)
public void print(int i)
public void print(long l)
public void print(float f)
等等
當(dāng)然,這些方法并不會(huì)真正的將數(shù)值的二進(jìn)制寫入文件,而只是將它們所對(duì)應(yīng)的字符串寫入文件,例如:
print(123);
最終寫入文件的不是 123 所對(duì)應(yīng)的二進(jìn)制表述,而僅僅是 123 這個(gè)字符串,這就是打印流。
PrintStream 使用的緩沖字符流實(shí)現(xiàn)所有的打印操作,如果指明了自動(dòng)刷新,則遇到換行符號(hào)「\n」會(huì)自動(dòng)刷新緩沖區(qū)。
所以說,PrintStream 集成了字節(jié)流和字符流中所有的輸出方法,其中 write 方法是用于字節(jié)流操作,print 方法用于字符流操作,這一點(diǎn)需要明確。
至于 PrintWriter,它就是全字符流,完全針對(duì)字符進(jìn)行操作,無論是 write 方法也好,print 方法也好,都是字符流操作。
總結(jié)一下,我們花了三篇文章講解了 Java 中的字節(jié)流和字符流操作,字節(jié)流基于字節(jié)完成磁盤和內(nèi)存之間的數(shù)據(jù)傳輸,最典型的就是文件字符流,它的實(shí)現(xiàn)都是本地方法。有了基本的字節(jié)傳輸能力后,我們還能夠通過緩沖來提高效率。
而字符流的最基本實(shí)現(xiàn)就是,InputStreamReader 和 OutputStreamWriter,理論上它倆就已經(jīng)能夠完成基本的字符流操作了,但也僅僅局限于最基本的操作,而構(gòu)造它們的實(shí)例所必需的就是「一個(gè)字節(jié)流實(shí)例」+「一種編碼格式」。
所以,字符流和字節(jié)流的關(guān)系也就如上述的等式一樣,你寫一個(gè)字符到磁盤文件中所必需的步驟就是,按照指定編碼格式編碼該字符,然后使用字節(jié)流將編碼后的字符二進(jìn)制寫入文件中,讀操作是相反的。
上述就是小編為大家分享的Java中怎么讀寫字符流文件了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。