一.介紹
字符流=字節(jié)流+編碼集,在實際讀取的時候其實字符流還是按照字節(jié)來讀取,但是會更具編碼集進(jìn)行查找編碼集字典解析相應(yīng)的字節(jié),使得一次讀取出一個字符;
轉(zhuǎn)換流就是原本是字節(jié)流,但是讀取到的數(shù)據(jù)是字符,所以我們希望使用字符流來進(jìn)行操作,那么就可以使用轉(zhuǎn)換流進(jìn)行轉(zhuǎn)換;
轉(zhuǎn)換流=字節(jié)流+編碼集。
轉(zhuǎn)換流的特點是可以指定編碼集。
轉(zhuǎn)換流的作用:
(1)從控制臺讀取數(shù)據(jù)輸入(鍵盤輸入),將它們寫入到文件(我們寫的是字符吧);
(2)當(dāng)對文件進(jìn)行解析的時候,如果涉及編碼,就需要使用轉(zhuǎn)換流進(jìn)行解碼----亂碼可不好玩。
二.知識點介紹
1、System.in與Ssytem.out
2、InputStreamReader與OutputStreamWriter
3、OutputStreamWriter類
4、轉(zhuǎn)換流和子類區(qū)別
5、編碼表
三.上課視頻對應(yīng)說明文檔
1、System.in與System.out
我們常見的System.in與System.out就是典型的字節(jié)流,可以直接與控制臺進(jìn)行數(shù)據(jù)傳輸。
如:OutputStream os = System.out;
方法阻塞:
當(dāng)調(diào)用這個方法時,程序會進(jìn)行等待,當(dāng)方法調(diào)用結(jié)束后,阻塞結(jié)束。
Scanner并不是鍵盤錄入,是文本掃描,可以根據(jù)綁定IO資源的不同,掃表不同的文本資源,如果給予文件,則掃描文件,如果給予System.in則掃描鍵盤錄入數(shù)據(jù)
代碼示例:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
/*
* 轉(zhuǎn)換流? 是 字符流的子類?
*
* 字符流? =? 字節(jié)流? + 編碼表 .? 保證了編碼表 和 文件的編碼 一致 ,能夠讀取數(shù)據(jù)成功.
*? ? ? ? ? ? 編碼表和 文件的編碼不一致, 亂碼.
* InputStreamReader(InputStream in, String charsetName) 創(chuàng)建使用指定字符集的InputStreamReader。
*/
public class Demo {
public static void main(String[] args) throws IOException {
//創(chuàng)建? InputStreamReader 對象
InputStreamReader isr = new InputStreamReader(new FileInputStream("bj.txt"), "utf8") ;
char ch? = (char)isr.read();
System.out.println(ch);
}
}
2、InputStreamReader與OutputStreamWriter
操作字節(jié)流對于程序來說過于繁瑣,程序員更偏愛操作字符。所以,當(dāng)面對一些字節(jié)流的操作時,我們可以將其轉(zhuǎn)換為字符流再進(jìn)行操作,這樣便非常方便了。
(1)Reader:
InputStreamReader可以完成字節(jié)輸入流轉(zhuǎn)換為字符輸入流
(2)Writer:
OutputStreamWriter可以完成字節(jié)輸出流轉(zhuǎn)換為字符輸出流。
由上邊可以知道,轉(zhuǎn)換流是字符流的一種,創(chuàng)建對象時傳入對應(yīng)字節(jié)流對象即可完成轉(zhuǎn)換動作。
轉(zhuǎn)換流同樣使用了包裝的思想,其構(gòu)造方法接收的同樣為IO流對象,并非某個文件資源。關(guān)閉轉(zhuǎn)換流的同時即關(guān)閉了對應(yīng)的字節(jié)流。
2.1、OutputStreamWriter類
查閱OutputStreamWriter的API介紹,OutputStreamWriter 是字符流通向字節(jié)流的橋梁:可使用指定的字符編碼表,將要寫入流中的字符編碼成字節(jié)。它的作用的就是,將字符串按照指定的編碼表轉(zhuǎn)成字節(jié),在使用字節(jié)流將這些字節(jié)寫出去。
代碼示例:
public static void writeCN() throws Exception {
//創(chuàng)建與文件關(guān)聯(lián)的字節(jié)輸出流對象
FileOutputStream fos = new FileOutputStream("c:\\cn8.txt");
//創(chuàng)建可以把字符轉(zhuǎn)成字節(jié)的轉(zhuǎn)換流對象,并指定編碼
OutputStreamWriter osw = new OutputStreamWriter(fos,"utf-8");
//調(diào)用轉(zhuǎn)換流,把文字寫出去,其實是寫到轉(zhuǎn)換流的高效區(qū)中
osw.write("你好");//寫入高效區(qū)。
osw.close();
}
OutputStreamWriter流對象,它到底如何把字符轉(zhuǎn)成字節(jié)輸出的呢?
其實在OutputStreamWriter流中維護自己的高效區(qū),當(dāng)我們調(diào)用OutputStreamWriter對象的write方法時,會拿著字符到指定的碼表中進(jìn)行查詢,把查到的字符編碼值轉(zhuǎn)成字節(jié)數(shù)存放到OutputStreamWriter高效區(qū)中。然后再調(diào)用刷新功能,或者關(guān)閉流,或者高效區(qū)存滿后會把高效區(qū)中的字節(jié)數(shù)據(jù)使用字節(jié)流寫到指定的文件中。
2.2、InputStreamReader類
查閱InputStreamReader的API介紹,InputStreamReader 是字節(jié)流通向字符流的橋梁:它使用指定的字符編碼表讀取字節(jié)并將其解碼為字符。它使用的字符集可以由名稱指定或顯式給定,或者可以接受平臺默認(rèn)的字符集。
代碼示例:
public class InputStreamReaderDemo {
public static void main(String[] args)throws IOException {
//演示字節(jié)轉(zhuǎn)字符流的轉(zhuǎn)換流
readCN();
}
public static void readCN() throws IOException{
//創(chuàng)建讀取文件的字節(jié)流對象
InputStream in = new FileInputStream("c:\\cn8.txt");
//創(chuàng)建轉(zhuǎn)換流對象
//InputStreamReader isr = new InputStreamReader(in);這樣創(chuàng)建對象,會用本地默認(rèn)碼表讀取,將會發(fā)生錯誤解碼的錯誤
InputStreamReader isr = new InputStreamReader(in,"utf-8");
//使用轉(zhuǎn)換流去讀字節(jié)流中的字節(jié)
int ch = 0;
while((ch = isr.read())!=-1){
System.out.println((char)ch);
}
//關(guān)閉流
isr.close();
}
}
注意:在讀取指定的編碼的文件時,一定要指定編碼格式,否則就會發(fā)生解碼錯誤,而發(fā)生亂碼現(xiàn)象。
3、轉(zhuǎn)換流和子類區(qū)別
發(fā)現(xiàn)有如下繼承關(guān)系:
Writer 字符輸出流
|- OutputStreamWriter? 轉(zhuǎn)換流(字符流—>字節(jié)流)(屬于字符輸出流, 可以指定字符編碼表,用來寫入數(shù)據(jù)到文件)
|--FileWriter 操作文件中字符輸出流,采用默認(rèn)的字符編碼表
Reader 字符輸入流
|- InputStreamReader: 轉(zhuǎn)換流(字節(jié)流à字符流)(屬于字符輸入流, 可以指定字符編碼表,用來從文件中讀數(shù)據(jù))
|--FileReader操作文件中字符輸入流,采用默認(rèn)的字符編碼表
父類和子類的功能有什么區(qū)別呢?
OutputStreamWriter和InputStreamReader是字符和字節(jié)的橋梁:也可以稱之為字符轉(zhuǎn)換流。字符轉(zhuǎn)換流原理:字節(jié)流+編碼表。
FileWriter和FileReader:作為子類,僅作為操作字符文件的便捷類存在。當(dāng)操作的字符文件,使用的是默認(rèn)編碼表時可以不用父類,而直接用子類就完成操作了,簡化了代碼。
InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"));//默認(rèn)字符集。
InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"),"GBK");//指定GBK字符集。
FileReader fr = new FileReader("a.txt");
這三句代碼的功能是一樣的,其中第三句最為便捷。
注意:一旦要指定其他編碼時,絕對不能用子類,必須使用字符轉(zhuǎn)換流。什么時候用子類呢?
條件:
(1)操作的是文件。
(2)使用默認(rèn)編碼。
總結(jié):
字節(jié)--->編碼表--->字符 : 看不懂的--->看的懂的。? 需要讀。輸入流。 InputStreamReader
字符--->編碼表--->字節(jié) : 看的懂的--->看不懂的。? 需要寫。輸出流。 OutputStreamWriter
4、編碼表
4.1、編碼表概念
在轉(zhuǎn)換流或者字符串的構(gòu)造方法當(dāng)中,我們發(fā)現(xiàn)有一個參數(shù)始終沒有使用,即字符串型的編碼集名稱。
如果沒有指定該名稱,則默認(rèn)為”GBK”,GBK就是編碼表的一種。
編碼表即字符與存儲數(shù)據(jù)的對應(yīng)關(guān)系表,每一個字符都對應(yīng)一個數(shù)字。
所以,有如下公式:
字符 = 字節(jié) + 編碼表
4.2、常用編碼表
(1)GBK:中文環(huán)境默認(rèn)碼表,中文碼表(2個字節(jié)對應(yīng)一個漢字)
(2)GB2312:與GBK基本相同
(3)UTF-8:萬國碼,JavaEE項目中的通用編碼表,包含各國文字編碼(一般3個字節(jié)對應(yīng)一個漢字)
(4)ISO8859-1:拉丁碼表,不包含中文,是西方較通用的碼表
(5)BIG-5:繁體字碼表
4.3、編碼表使用
亂碼:當(dāng)字符與字節(jié)轉(zhuǎn)換過程中使用了不同的碼表,會造成亂碼的情況。
在字符串中:
當(dāng)我們將字符串轉(zhuǎn)為對應(yīng)的數(shù)字字節(jié)時,需要指定碼表,則存儲了為該字符該碼表對應(yīng)的數(shù)字字節(jié),如果使用了其他碼表重寫翻譯回字符串,則拼寫的新字符串會亂碼。
在IO中:
與字符串編碼表使用類似,當(dāng)以某個碼表寫出字節(jié)數(shù)據(jù)時,又使用另外碼表展示,會出現(xiàn)亂碼。