摘要:如果不指定字符集,則使用系統(tǒng)默認(rèn)字符編碼,系統(tǒng)的默認(rèn)字符編碼一般是。所以更準(zhǔn)確的說,是將一個(gè)字節(jié)輸入流按照給定的字符編碼來解碼,從而得到一個(gè)字符輸入流。當(dāng)然,缺點(diǎn)就是不能選擇使用的字符編碼。
相對(duì)于Python和 C來說,Java的I/O操作API比較復(fù)雜,因此本文打算做個(gè)簡單的介紹。
1. I/O分類總的來說Java的I/O按照處理數(shù)據(jù)的粒度和方向來劃分,一共可以分為4類:
基于字節(jié)
輸入 InputStream
輸出 OutputStream
基于字符
輸入 Reader
輸出 Writer
使用原則:要讀寫二進(jìn)制數(shù)據(jù)時(shí),使用基于字節(jié)的API;要讀寫文本數(shù)據(jù)時(shí),使用基于字符的API,文本數(shù)據(jù)操作需要指定字符編碼。強(qiáng)調(diào)一點(diǎn),本文說的字符是指Java的數(shù)據(jù)類型char類型,并不是C語言中的char類型(該類型長度為8位,一個(gè)字節(jié)),即Java中的一個(gè)字符有可能包含多個(gè)字節(jié)。
這里提到的InputStream, OutputStream, Reader 和Writer 是Java API里的4個(gè)抽象類,不能用來初始化新的實(shí)例,我們只能從這4個(gè)類的子類(或者后代類)來創(chuàng)建I/O操作的實(shí)例,而且正是這些子類實(shí)現(xiàn)類不同介質(zhì)和不同功能的I/O。另外,兩個(gè)用于輸入的抽象類都定義了一個(gè)抽象的int read()方法,兩個(gè)用于輸出的抽象類都定義了一個(gè)抽象的void write()方法,這些抽象方法則由子類來實(shí)現(xiàn)。
2. 文件I/O的使用Java I/O可以可以應(yīng)用于各種輸入輸出介質(zhì),包括文件、控制臺(tái)(也是文件的一種)、內(nèi)存、網(wǎng)絡(luò)等。這里先介紹文件I/O,搞懂了文件I/O相關(guān)的API后,其他的I/O就都好理解了。
最基本方法根據(jù)第一節(jié)的分類,文件I/O的API也分為基于字節(jié) 和基于字符 的兩大類。我們先來看最基礎(chǔ)的文件I/O的類:
基于字節(jié)
FileInputStream
該類的read()方法每次從文件讀取一個(gè)字節(jié)。
FileOutputStream
該類的write()方法每次向文件寫入一個(gè)字節(jié)。
基于字符
InputStreamReader
該類的read()方法每次從一個(gè)輸入流中讀取一個(gè)字符。該類的構(gòu)造函數(shù)的第一個(gè)參數(shù)是一個(gè)InputStream實(shí)例,也就是將說該類將一個(gè)基于字節(jié)的輸入流變成一個(gè)基于字符的輸入流。如果不指定字符集,則使用系統(tǒng)默認(rèn)字符編碼,Ubuntu系統(tǒng)的默認(rèn)字符編碼一般是UTF-8。所以更準(zhǔn)確的說,是將一個(gè)字節(jié)輸入流按照給定的字符編碼來解碼,從而得到一個(gè)字符輸入流。
OutputStreamWriter
該類的write()方法每次向一個(gè)輸出流中寫入一個(gè)字符。該類的構(gòu)造函數(shù)的第一個(gè)參數(shù)是一個(gè)OutputStream實(shí)例,也就是說該類將一個(gè)基于字節(jié)的輸出流變成一個(gè)基于字符的輸出流。如果不指定字符集,則使用系統(tǒng)默認(rèn)字符編碼,Ubuntu系統(tǒng)的默認(rèn)字符編碼一般是UTF-8。所以更準(zhǔn)確的說,是將一個(gè)字節(jié)輸出流按照給定的字符編碼來編碼(把要輸出的字符轉(zhuǎn)換成二進(jìn)制數(shù)據(jù)),從而得到一個(gè)字符輸出流。
FileReader
該類的read()方法每次從文件讀取一個(gè)字符。這個(gè)類的作用等于如下代碼:
InputStreamReader in = new InputStreamReader(new FileInputStream(pathToFile));
也就是先從一個(gè)文件創(chuàng)建一個(gè)字節(jié)輸入流,然后再采用系統(tǒng)默認(rèn)編碼方式轉(zhuǎn)換成一個(gè)字符輸入流。當(dāng)然,缺點(diǎn)就是不能選擇使用的字符編碼。
FileWriter
該類的write()方法每次向文件寫入一個(gè)字符。這個(gè)類的作用等于如下代碼:
`OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(pathToFile));
具體解釋和FileReader類似。
介紹完常用類,我們來講下如何使用。
二進(jìn)制數(shù)據(jù)讀寫(基于字節(jié))
如果要從文件讀入二進(jìn)制數(shù)據(jù),則先構(gòu)造一個(gè)FileInputStream實(shí)例,然后調(diào)用read()方法每次讀入一個(gè)字節(jié),也可以調(diào)用read方法的其他實(shí)現(xiàn),每次讀入多個(gè)字節(jié)。
如果要向文件寫入二進(jìn)制數(shù)據(jù),則先構(gòu)造一個(gè)FileOutputStream示例,然后調(diào)用write()方法每次寫入一個(gè)字節(jié),也可以調(diào)用write方法的其他實(shí)現(xiàn),每次寫入多個(gè)字節(jié)。
文本數(shù)據(jù)讀寫(基于字符)
如果要從文件讀入文本數(shù)據(jù),可以選擇如下兩種方式:
構(gòu)造一個(gè)FileReader實(shí)例,使用系統(tǒng)默認(rèn)編碼,然后調(diào)用read()方法每次讀入一個(gè)字符,也可以調(diào)用read方法的其他實(shí)現(xiàn),每次讀入多個(gè)字符。
采用如下方式構(gòu)造一個(gè)InputStreamReader實(shí)例:new InputStreamReader(new FileInputStream(pathToFile), codecName),使用指定的字符編碼,然后調(diào)用read()方法每次讀入一個(gè)字符,也可以調(diào)用read方法的其他實(shí)現(xiàn),每次讀入多個(gè)字符。
如果要向文件寫入文本數(shù)據(jù),可以選擇如下兩種方式:
構(gòu)造一個(gè)FileWriter實(shí)例,使用系統(tǒng)默認(rèn)編碼,然后調(diào)用write()方法每次寫入一個(gè)字符,也可以調(diào)用write方法的其他實(shí)現(xiàn),每次寫入多個(gè)字符。
采用如下方式構(gòu)造一個(gè)OutputStreamwriter實(shí)例:new OutputStreamWriter(new FileOutputStream(pathToFile), codecName),使用指定的字符編碼,然后調(diào)用write()方法每次寫入一個(gè)字符,也可以調(diào)用write方法的其他實(shí)現(xiàn),每次寫入多個(gè)字符。
來看下代碼實(shí)例,文件~/tmp/words中存放了一行中文字符,采用的是UTF-8編碼方式:
~/tmp/words 你好
下面的代碼展示了基于字節(jié)和基于字符兩種方式讀取文件內(nèi)容的區(qū)別:
import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; public class Hello { public static void main(String[] args) { String wordFileName = "~/tmp/words"; String wordFilePath = wordFileName.replace("~", System.getProperty("user.home")); try { // byte-based input System.out.println("InputStream"); FileInputStream fin = new FileInputStream(wordFilePath); int c; while ((c = fin.read()) != -1) { System.out.printf("%02x ", c); } System.out.println(); // char-based input System.out.println("Reader"); InputStreamReader rin = new InputStreamReader( new FileInputStream(wordFilePath), "UTF-8"); while ((c = rin.read()) != -1) { System.out.printf("%02x ", c); } System.out.println(); } catch (IOException e) { System.out.println(e); System.exit(1); } } }
運(yùn)行結(jié)果為:
InputStream e4 bd a0 e5 a5 bd 0a Reader 4f60 597d 0a
可以看出,基于字節(jié)的輸入每次讀入一個(gè)字節(jié),兩個(gè)漢字一共6個(gè)字節(jié),換行符一個(gè)字節(jié)(0x0a),一共讀了7次。而基于字符的輸入每次讀入后保存的結(jié)果為兩個(gè)字節(jié)(因?yàn)镴ava內(nèi)部都是UTF-16表示的,因此從文件讀入字符的時(shí)候已經(jīng)做了UTF-8到UTF-16轉(zhuǎn)換),兩個(gè)漢字和一個(gè)換行符一共讀了三次。
更方便的方法上一小節(jié)說的方法其實(shí)相當(dāng)于C語言中的中的如下函數(shù):
FileInputStream: read, fgetc, fread等
FileOutputStream: write, fputc, fwrite等
InputStreamReader, FileReader: 如果文件不是ASCII編碼的,則相當(dāng)于fgetwc(wchar.h文件中定義)等;如果文件是ASCII編碼,則相當(dāng)于fgetc等。
OutputStreamWriter, FileWriter: 同上,相當(dāng)于fputwc和fputc等。
這些只能基于單個(gè)字符或者單個(gè)字節(jié)進(jìn)行輸入輸出的API使用起來比較麻煩,比較使用用來操作二進(jìn)制數(shù)據(jù)。本節(jié)會(huì)介紹一些更方便的文件I/O方法。
讀寫二進(jìn)制文件在不考慮對(duì)象序列化等更復(fù)雜的方法時(shí),Java也提供了DataInputStream 和DataOutputStream 的類,用來從二進(jìn)制流中讀取Java的基本類型數(shù)據(jù)或者向二進(jìn)制流中寫入基本類型數(shù)據(jù),比如讀一個(gè)整型和寫入一個(gè)整型。
DataInputStream是FilterInputStream的子類,DataOutputStream則是FilterOutputStream的子類,都屬于過濾類的流,其作用是從一個(gè)流讀入數(shù)據(jù),然后轉(zhuǎn)換一下表達(dá)方式在輸出,比如DataInputStream可以從FileInputStream連續(xù)讀出4個(gè)字節(jié),然后轉(zhuǎn)換成一個(gè)整型返回給調(diào)用者。
讀寫二進(jìn)制文件更高級(jí)的就是各種對(duì)象序列化方法了,這個(gè)本文不討論。
讀寫文本文件讀寫文本文件我們很習(xí)慣于按照行來進(jìn)行讀寫,比如C語言的scanf 和printf 函數(shù)。在Java中分別使用下面兩個(gè)類來進(jìn)行:
Scanner: 有眾多構(gòu)造函數(shù),其中一個(gè)可以從指定輸入流,然后實(shí)現(xiàn)類似scanf函數(shù)的效果。
PrintWritter: 該類在一個(gè)Writer的基礎(chǔ)上實(shí)現(xiàn)了常用的print, println和printf接口。
如下代碼從一個(gè)文件構(gòu)造一個(gè)Scanner實(shí)例,然后你就可以調(diào)用Scanner類的next, nextInt, nextLine的函數(shù)來從文件讀取輸入:
Scanner in = new Scanner(new FileInputStream(pathToFile));
Scanner類還有其他構(gòu)造函數(shù),能夠指定字符編碼,以及從其他類型的參數(shù)構(gòu)造出一個(gè)實(shí)例。
如下代碼從一個(gè)文件構(gòu)造出一個(gè)PrintWriter實(shí)例,然后你就可以調(diào)用print, println和printf了:
PrintWriter out = new PrintWriter(new FileWriter(pathToFile));
不過還有個(gè)更方便的構(gòu)造函數(shù):
PrintWriter out = new PrintWriter(pathToFile);3. 標(biāo)準(zhǔn)輸入,標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤
System類的三個(gè)成員in, out, err分別系統(tǒng)的標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤,通過查看源碼可以發(fā)現(xiàn)他們的定義是這樣的:
public final class System { ... public static final InputStream in = null; public static final PrintStream out = null; public static final PrintStream err = null; ... }
因此要從標(biāo)準(zhǔn)輸出讀取數(shù)據(jù)的化,可以直接使用InputStream的read方法,或者構(gòu)造一個(gè)Scanner實(shí)例來使用:
System.in.read(); Scanner in = new Scanner(System.in);
標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤則是兩個(gè)PrintStream類的實(shí)例,查看代碼可以發(fā)現(xiàn),這個(gè)類的實(shí)現(xiàn)基本上和PrintWriter一樣,也就是說可以直接使用print, println和printf等方法進(jìn)行數(shù)據(jù)輸出。還有,PrintStream類是繼承自FilterOutputStream,因此write方法也是可用的。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/64168.html
摘要:特點(diǎn)面向塊的傳統(tǒng)是面向流的。有四個(gè)基本屬性容量,能夠容納的最大元素?cái)?shù)目,在創(chuàng)建時(shí)設(shè)定并不能更改中有效位置數(shù)目,不能對(duì)超過中的區(qū)域進(jìn)行讀寫。與緩沖區(qū)不同,通道主要由接口指定。方法獲取支持的所有字符集獲取實(shí)例編解碼文件鎖進(jìn)程級(jí)支持文件鎖定功能。 簡介 NIO的所有類都被放在java.nio包或其子包下。 特點(diǎn) 面向塊的I/O:傳統(tǒng)JavaIO是面向流的I/O。流I/O一次處理一個(gè)字節(jié)。N...
摘要:在中一般來說通過來創(chuàng)建所需要的線程池,如高并發(fā)原理初探后端掘金閱前熱身為了更加形象的說明同步異步阻塞非阻塞,我們以小明去買奶茶為例。 AbstractQueuedSynchronizer 超詳細(xì)原理解析 - 后端 - 掘金今天我們來研究學(xué)習(xí)一下AbstractQueuedSynchronizer類的相關(guān)原理,java.util.concurrent包中很多類都依賴于這個(gè)類所提供的隊(duì)列式...
摘要:在中一般來說通過來創(chuàng)建所需要的線程池,如高并發(fā)原理初探后端掘金閱前熱身為了更加形象的說明同步異步阻塞非阻塞,我們以小明去買奶茶為例。 AbstractQueuedSynchronizer 超詳細(xì)原理解析 - 后端 - 掘金今天我們來研究學(xué)習(xí)一下AbstractQueuedSynchronizer類的相關(guān)原理,java.util.concurrent包中很多類都依賴于這個(gè)類所提供的隊(duì)列式...
摘要:操作指引該文件服務(wù)組件的使用需要分為兩個(gè)部分,一個(gè)是服務(wù)端配置與啟動(dòng),一個(gè)是客戶端的配置與啟動(dòng)。在調(diào)用文件服務(wù)返回的路徑的時(shí)候,需要用到服務(wù)端訪問文件的地址,進(jìn)而訪問相應(yīng)的文件內(nèi)容。 本文所述文件服務(wù)組件在筆者此前一篇文章中已有闡述(基于netty的文件上傳下載組件),不過本文將基于之前這個(gè)實(shí)現(xiàn)再次進(jìn)行升級(jí)改造,利用基于注解的方式進(jìn)行自動(dòng)裝配。 1. 簡介 1.1 Netty簡介 Ne...
摘要:緩沖區(qū)一個(gè)對(duì)象是固定數(shù)量的數(shù)據(jù)的容器。緩沖區(qū)的工作與通道緊密聯(lián)系。對(duì)于操作而言,從通道讀取的數(shù)據(jù)會(huì)按順序被散布稱為到多個(gè)緩沖區(qū),將每個(gè)緩沖區(qū)填滿直至通道中的數(shù)據(jù)或者緩沖區(qū)的最大空間被消耗完。文件通道總是阻塞式的,因此不能被置于非阻塞模式。 簡介 從JDK1.4開始,java中提供一個(gè)種叫NIO(Non-Blocking IO)的IO處理機(jī)制。與以往的標(biāo)準(zhǔn)IO機(jī)制(BIO,Blockin...
閱讀 2428·2021-11-25 09:43
閱讀 1203·2021-09-07 10:16
閱讀 2623·2021-08-20 09:38
閱讀 2947·2019-08-30 15:55
閱讀 1467·2019-08-30 13:21
閱讀 897·2019-08-29 15:37
閱讀 1450·2019-08-27 10:56
閱讀 2100·2019-08-26 13:45