目錄
第1關(guān):什么是IO流
相關(guān)知識(shí)
什么是字節(jié)
什么是字符
什么是IO流
第2關(guān):字節(jié)流-輸入輸出
相關(guān)知識(shí)
輸入流
輸出流
最佳實(shí)踐
編程要求
?第3關(guān):字符流 - 輸入輸出
相關(guān)知識(shí)
Writer
Reader
編程要求
?第4關(guān):復(fù)制文件
相關(guān)知識(shí)
read()方法
write()方法
使用字節(jié)流讀寫文件
擴(kuò)展
編程要求
第1關(guān):什么是IO流
相關(guān)知識(shí)
什么是字節(jié)
字節(jié)是指一小組相鄰的二進(jìn)制數(shù)碼。通常是8
位作為一個(gè)字節(jié)。它是構(gòu)成信息的一個(gè)小單位,并作為一個(gè)整體來(lái)參加操作,比字小,是構(gòu)成字的單位。
字節(jié)(Byte
) 是一種計(jì)量單位,表示數(shù)據(jù)量的多少,它是計(jì)算機(jī)信息技術(shù)用于計(jì)量存儲(chǔ)容量的一種計(jì)量單位.
什么是字符
我們想象一下,給你一串二進(jìn)制碼,要你來(lái)分辨它是什么含義,是代表數(shù)字還是字母還是漢字,你能有效的分辨嗎?
顯然不能,一般來(lái)說(shuō),我們是比較難以理解一串二進(jìn)制碼代表的含義的,而且一串二進(jìn)制碼是代表什么含義也無(wú)法很直觀的表示出來(lái)。
我們比較好識(shí)別的是文字,字母和符號(hào)。
所以就有了字符,字符是指計(jì)算機(jī)中使用的文字和符號(hào),比如1、2、3、A、B、C、~!·#¥%……—*()——+
、等等。
字符在計(jì)算機(jī)中可以看做:字節(jié)+編碼表
我們知道,計(jì)算機(jī)是只識(shí)別二進(jìn)制的,但是我們?nèi)粘2僮麟娔X,需要輸入文字,字母,數(shù)字這些,我們不可能先去記住一串二進(jìn)制數(shù)字,比如說(shuō)A
這個(gè)字母的二進(jìn)制是什么,因?yàn)檫@樣太麻煩,也記不住,所以編碼表,就誕生了,編碼表的作用就是在我們進(jìn)行輸入的時(shí)候,將我們輸入的字符轉(zhuǎn)換成計(jì)算機(jī)能識(shí)別的二進(jìn)制,在我們閱讀數(shù)據(jù)的時(shí)候,將二進(jìn)制轉(zhuǎn)換成我們?nèi)四茏R(shí)別的文字字母和數(shù)字。
最先普及的就要數(shù)ASCLL
碼表了,ASCLL
碼表是美國(guó)信息交換標(biāo)準(zhǔn)代碼,是基于拉丁字母的一套電腦編碼系統(tǒng),主要用于顯示現(xiàn)代英語(yǔ)和其他西歐語(yǔ)言。
看到這你肯定會(huì)有疑問(wèn),這ASCLL
碼表只有英語(yǔ)和西歐語(yǔ)呀,那漢語(yǔ)呢,其他語(yǔ)言呢?自從ASCLL
碼表推出之后,很多國(guó)家也都推出了本國(guó)語(yǔ)言的編碼表。像中國(guó)就有GB2312
,GBK
等等。
現(xiàn)在我們一起設(shè)想一個(gè)場(chǎng)景,當(dāng)我們編輯一個(gè)文本文件,輸入了很多字符,這些字符都用ASCLL
碼表編碼,然后我們查看這個(gè)文本文件的時(shí)候,是使用的GBK
碼表解碼,會(huì)出現(xiàn)什么問(wèn)題嗎?
相信你已經(jīng)有答案了,這會(huì)出現(xiàn)軟件開(kāi)發(fā)中非常常見(jiàn)的問(wèn)題:亂碼。
當(dāng)我們對(duì)字節(jié)進(jìn)行編碼的時(shí)候使用的是一種編碼表,而解碼的時(shí)候使用的是另一種編碼表的時(shí)候,就會(huì)出現(xiàn)亂碼的問(wèn)題了,是因?yàn)槊恳粋€(gè)編碼表,它的字符對(duì)應(yīng)二進(jìn)制的字節(jié)是不一致的。但是互聯(lián)網(wǎng)是一個(gè)互聯(lián)互通的平臺(tái),所以如果每個(gè)國(guó)家都使用自己的一套編碼器,就會(huì)出現(xiàn)許多問(wèn)題。
在1992
年的時(shí)候,推出了UTF-8
編碼規(guī)范,是一種針對(duì)Unicode
的可變長(zhǎng)度字符編碼,又稱萬(wàn)國(guó)碼,UTF-8
用1到6
個(gè)字節(jié)編碼Unicode
字符。用在網(wǎng)頁(yè)上可以統(tǒng)一頁(yè)面顯示中文簡(jiǎn)體繁體及其它語(yǔ)言(如英文,日文,韓文)。
UTF-8
也是我們目前在應(yīng)用開(kāi)發(fā)中使用的最多的編碼格式。
Java中默認(rèn)采用的是Unicode編碼格式(具體來(lái)說(shuō)是UTF-16編碼)。
什么是IO流
IO
流中的IO
是Input
,Output
,輸入和輸出的意思,是用來(lái)處理設(shè)備與設(shè)備之間的數(shù)據(jù)傳輸?shù)模粌H能處理內(nèi)部設(shè)備(比如CPU
、GPU
、內(nèi)存),還能處理外部設(shè)備(比如手機(jī)和PC
,客戶端與服務(wù)器)。
在Java中定義數(shù)據(jù)按照流向,分為輸入流和輸出流。
首先我們來(lái)了解輸入流,從字面上就很容易理解,凡是從外部流入的數(shù)據(jù)都可以通過(guò)輸入流來(lái)處理。比如讀取文件。
輸出流,就表示從內(nèi)部流出的數(shù)據(jù),比如:我們編輯了一個(gè)文本文件,當(dāng)我們按下ctrl+s
的時(shí)候,就將該文件從內(nèi)存保存到了硬盤,這就是一個(gè)將數(shù)據(jù)從內(nèi)存中輸出到硬盤的過(guò)程。
除了輸出和輸入流,流按照操作的數(shù)據(jù)還分為:字節(jié)流和字符流。
總體結(jié)構(gòu)如下圖:
好了,IO
流的簡(jiǎn)單介紹就到這里啦,使用本關(guān)所學(xué)知識(shí)來(lái)完成選擇題吧。
第2關(guān):字節(jié)流-輸入輸出
相關(guān)知識(shí)
輸入流
我們通過(guò)一個(gè)示例,來(lái)看看輸入流應(yīng)該如何使用,首先我們?cè)?code>D盤下創(chuàng)建一個(gè)hello.txt
文件。輸入文本Hello Java Hello InputStream
。
在main
方法中加入如下代碼:
輸出:
Hello Java Hello InputStream
代碼解釋:
這個(gè)例子我們主要目的是,讀取文件中的數(shù)據(jù)并將數(shù)據(jù)顯示在控制臺(tái)。
實(shí)現(xiàn)步驟是:首先讀取文件轉(zhuǎn)換成文件輸入流(FileInputStream
),然后定義一個(gè)字節(jié)數(shù)組作為容器用來(lái)存儲(chǔ)即將讀取到的數(shù)據(jù)。fs.read(b)
函數(shù)的作用是將數(shù)據(jù)讀取到b
數(shù)組中,最后通過(guò)編碼表,將字節(jié)數(shù)組編碼成字符。
輸出流
我們使用輸出流將字符串hello educoder
寫入到一個(gè)文件中:
運(yùn)行這段代碼,打開(kāi)D
盤下你會(huì)發(fā)現(xiàn)test.txt
文件被創(chuàng)建了,并且文件的內(nèi)容是hello educoder
。
代碼解釋:
最佳實(shí)踐
上面作為示例的兩段代碼都是存在很大問(wèn)題的,什么問(wèn)題呢?
因?yàn)樵贘ava中對(duì)于流的操作是非常消耗資源的,如果我們使用流對(duì)一個(gè)資源進(jìn)行操作了之后卻沒(méi)有釋放它的資源,這就會(huì)造成系統(tǒng)資源的浪費(fèi),如果積累了很多這種空置的資源,最后可能會(huì)導(dǎo)致系統(tǒng)崩潰。
上述代碼的最佳實(shí)踐為:
OutputStream out = null;
try {
String file = "D://test.txt";
out = new FileOutputStream(file);
String str = "hello educoder";
byte[] b = str.getBytes();
out.write(b);
out.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (out != null) {
try {
out.close(); // 釋放該輸出流
} catch (IOException e) {
e.printStackTrace();
}
}
}
核心就是在使用完流之后,釋放它所占用的資源。
編程要求
請(qǐng)仔細(xì)閱讀右側(cè)代碼,根據(jù)方法內(nèi)的提示,在Begin - End
區(qū)域內(nèi)進(jìn)行代碼補(bǔ)充,具體任務(wù)如下:
- 讀取
src/step2/input/
目錄下的task.txt
文件信息并輸出到控制臺(tái),使用Java代碼將字符串learning practice
寫入到src/step2/output/
目錄下的output.txt
,若文件目錄不存在,則創(chuàng)建該目錄。
注意:臨時(shí)字節(jié)數(shù)組需要定義長(zhǎng)度為8
位,否則會(huì)有空格。
package step2;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class Task {
public void task() throws IOException{
/********* Begin *********/
File file = new File("src/step2/input/task.txt");
FileInputStream fs = new FileInputStream(file);
byte[] b = new byte[8];
fs.read(b); //輸入流數(shù)據(jù)讀入到字節(jié)數(shù)組
String str = new String(b,"UTF-8"); //指定字符格式
System.out.println(str);
File dir = new File("src/step2/output");
if(!dir.exists()){
dir.mkdir();
}
FileOutputStream out = new FileOutputStream("src/step2/output/output.txt");
String str1 = "learning practice";
byte[] c = str1.getBytes();
out.write(c); //字節(jié)寫出到文件
out.flush(); //刷新緩沖區(qū)數(shù)據(jù),類似于保存文件
fs.close(); //釋放流
out.close(); //釋放流
/********* End *********/
}
}
?第3關(guān):字符流 - 輸入輸出
相關(guān)知識(shí)
Writer
字符流的使用很簡(jiǎn)單,和字節(jié)輸入流類似,以FileWriter
舉例:
執(zhí)行上述代碼即可看到在D
盤下創(chuàng)建了一個(gè)名為hello.txt
的文件,文件的內(nèi)容為hello
。
上面代碼fw.flush()
和fw.close()
也可以省略fw.flush()
,只寫fw.close()
就可以了,但是都省略是不對(duì)的,如果都省略你會(huì)發(fā)現(xiàn)文本沒(méi)有寫入到hello.txt
文件。
Reader
Reader
的使用也很簡(jiǎn)單,以FileReader
為例:
輸出:
hello
+ 1019
個(gè)空格
使用上述代碼的會(huì)輸出hello.txt
中的內(nèi)容,但是會(huì)有一個(gè)問(wèn)題:輸出hello
的同時(shí)還輸出了1019
個(gè)空格,這是什么原因呢,如何解決這些問(wèn)題呢?請(qǐng)你思考。
我們?cè)谙乱魂P(guān)中揭曉答案。
編程要求
請(qǐng)仔細(xì)閱讀右側(cè)代碼,根據(jù)方法內(nèi)的提示,在Begin - End
區(qū)域內(nèi)進(jìn)行代碼補(bǔ)充,具體任務(wù)如下:
-
將
src/step3/input/
目錄下的input.txt
文件復(fù)制到src/step3/output/
目錄下; -
復(fù)制的新文件命名為
output.txt
; -
input.txt
文件中只有8
個(gè)字符。
package step3;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class Task {
public void task() throws IOException{
/********* Begin *********/
String file1 = "src/step3/input/input.txt";
FileReader fr = new FileReader(file1);
char[] ch = new char[8];
fr.read(ch); //文件數(shù)據(jù)讀到數(shù)組中
String file2 = "src/step3/output/output.txt";
FileWriter fw = new FileWriter(file2);
fw.write(ch); //寫出到文件
fr.close();
fw.flush(); //刷新流
fw.close(); //釋放流
/********* End *********/
}
}
?第4關(guān):復(fù)制文件
相關(guān)知識(shí)
上一關(guān)中最后,我們遇到了一個(gè)問(wèn)題:hello.txt
文件只有五個(gè)字符,而用來(lái)存儲(chǔ)字符的數(shù)組有1024
個(gè)字符,直接使用FileReader
的read()
方法讀取然后輸出就會(huì)有1019
個(gè)空字符,如何來(lái)解決這個(gè)問(wèn)題呢?
很容易想到的方法就是,我們定義一個(gè)長(zhǎng)度為5
的字符數(shù)組就可以了,這樣確實(shí)可以暫時(shí)解決問(wèn)題,可是我們往往不知道讀取的文件有多大,如果文件中不止5
個(gè)字符,而是有幾萬(wàn)個(gè)字符我們又應(yīng)該怎么辦呢?
這就需要我們深入的了解IO
流的常用函數(shù)了。
read()方法
我們來(lái)看read
方法的詳細(xì)解釋:
理解了read
方法,之前的問(wèn)題就好解決了。
代碼:
String file = "D://hello.txt";
FileReader fr = new FileReader(file);
char[] cbuf = new char[1024];
int len = fr.read(cbuf);//將數(shù)據(jù)讀入到cbuf中并返回讀取到的數(shù)據(jù)長(zhǎng)度
StringBuilder builder = new StringBuilder();
builder.append(cbuf,0,len); //將cbuf 0 到len長(zhǎng)度的數(shù)據(jù)添加到builder
System.out.println(builder.toString());
運(yùn)行這段代碼,我們會(huì)發(fā)現(xiàn)輸出是正確的,沒(méi)有再打印出多余的空格。
可能我們又會(huì)有疑問(wèn)了,如果文本文件大于1K
,這段代碼肯定就行不通了,怎么辦呢?
很簡(jiǎn)單,加個(gè)循環(huán)就可以啦:
String file = "D://hello.txt";
FileReader fr = new FileReader(file);
char[] cbuf = new char[1024];
int len = 0; // 每次讀取的長(zhǎng)度
StringBuilder builder = new StringBuilder();
while ((len = fr.read(cbuf)) != -1) {
builder.append(cbuf,0,len);
}
System.out.println(builder.toString());
這樣修改之后我們就可以讀取任意的文件,并將其內(nèi)容輸出到控制臺(tái)了。
write()方法
write()
方法有兩種常用的重載方法:
理解了這兩種方法,我們現(xiàn)在如果要復(fù)制一個(gè)文本文件就很方便了,現(xiàn)在我們就來(lái)將D
盤下hello.txt
文件復(fù)制到E
盤下,并重命名為abc.txt
:
FileReader fr = new FileReader("D://hello.txt"); //定義FileReader讀取文件
int len = 0; //每次讀取的字符數(shù)量
char[] cbuf = new char[1024]; //每次讀取數(shù)據(jù)的緩沖區(qū)
FileWriter fw = new FileWriter("E://abc.txt"); //定義FileWriter寫文件
while((len = fr.read(cbuf)) != -1){
fw.write(cbuf,0,len);
}
fw.close(); //釋放資源 刷新緩沖區(qū)
fr.close();
這段代碼就是一個(gè)邊讀邊寫的過(guò)程,運(yùn)行之后我們發(fā)現(xiàn)E
盤下已經(jīng)有了abc.txt
文件并且內(nèi)容和hello.txt
一致。
使用字節(jié)流讀寫文件
到目前為止我們一直操作的都是文本文件,不過(guò)我們計(jì)算機(jī)中存儲(chǔ)的文件可不止有文本文件,還有很多其他類型的,比如圖片,視頻,等等。
如果要對(duì)非文本類型的文件進(jìn)行操作,應(yīng)該怎么做呢?這個(gè)時(shí)候字符流還能不能派上用場(chǎng)呢?
答案是否定的,字符流只適用于操作字符類型的文件,不能操作非字符類型的。
所以這個(gè)時(shí)候應(yīng)該用什么來(lái)操作呢?
相信你已經(jīng)想到了:字節(jié)流。
是的我們需要使用字節(jié)流來(lái)操作非字符類文件。
接下來(lái),我們使用字節(jié)流來(lái)復(fù)制一個(gè)圖片文件,代碼:
FileInputStream fs = new FileInputStream("D://user.jpg"); //定義文件輸入流讀取文件信息
FileOutputStream fos = new FileOutputStream("E://new.jpg");//定義文件輸出流寫文件
int len = 0; //每次讀取數(shù)據(jù)的長(zhǎng)度
byte[] bys = new byte[1024]; //數(shù)據(jù)緩沖區(qū)
while( (len = fs.read(bys)) != -1){
fos.write(bys, 0, len);
}
//釋放資源 刷新緩沖區(qū)
fs.close();
fos.close();
運(yùn)行即可看到E
盤下生成了一個(gè)名為new.jpg
的文件,且內(nèi)容和user.jpg
一致
可以發(fā)現(xiàn)上述代碼和之前的字符流很像,確實(shí)原理都是類似的。
可能學(xué)到這,你會(huì)有很多疑問(wèn):
-
字節(jié)流既然可以用來(lái)讀取非字符構(gòu)成的文件,那可以讀取字符類型的文件嗎? 答案是可以的,字節(jié)流可以操作所有類型的文件,因?yàn)橛?jì)算機(jī)中的數(shù)據(jù)都是以字節(jié)的方式存儲(chǔ)的;
-
既然字節(jié)流可以用來(lái)操作所有的文件,那還要字符流干啥咧? 因?yàn)樽址鞑僮髯址愋偷臄?shù)據(jù)和文件要比字節(jié)流快很多。
擴(kuò)展
使用BufferedReader
讀取字符文件的速度要比我們之前使用的字節(jié)流和FileReader快很多,示例代碼:
BufferedReader bf = new BufferedReader(new FileReader("D://hello.txt"));
BufferedWriter writer = new BufferedWriter(new FileWriter("D://abc.txt"));
String str = "";
while( (str = bf.readLine()) != null){
writer.write(str);
}
bf.close();
writer.close();
編程要求
請(qǐng)仔細(xì)閱讀右側(cè)代碼,根據(jù)方法內(nèi)的提示,在Begin - End
區(qū)域內(nèi)進(jìn)行代碼補(bǔ)充,具體任務(wù)如下:
-
復(fù)制
src/step4/input/
目錄下的input.txt
文件到src/step4/output/
目錄下,新文件命名為output.txt
;文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-769236.html -
復(fù)制
src/step4/input/
目錄下的input.jpg
文件到src/step4/output/
目錄下,新文件命名為output.jpg
。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-769236.html
package step4;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class Task {
public void task() throws IOException{
/********* Begin *********/
String file1 = "src/step4/input/input.txt";
FileInputStream fr = new FileInputStream(file1);
byte[] b = new byte[1024];
int len = 0;
String file2 = "src/step4/output/output.txt";
FileOutputStream fw = new FileOutputStream(file2);
while((len = fr.read(b))!=-1){ //將數(shù)據(jù)讀入到b中并返回讀取到的數(shù)據(jù)長(zhǎng)度
fw.write(b,0,len);
}
fr.close();
fw.close();
String file3 = "src/step4/input/input.jpg";
String file4 = "src/step4/output/output.jpg";
FileInputStream fs = new FileInputStream(file3);
FileOutputStream fos = new FileOutputStream(file4);
len = 0;
byte[] bys = new byte[1024];
while( (len = fs.read(bys)) != -1){
fos.write(bys, 0, len);
}
fs.close();
fos.close();
/********* End *********/
}
}
到了這里,關(guān)于Educoder/頭歌JAVA——JAVA高級(jí)特性:IO流的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!