天天看點

IO流複習總結(三) 字元流、編碼表

字元流

我們位元組流讀字元資料,需要人工去做位元組和字元資料的轉換。我們自己轉換,我們不清楚哪些位元組應該一起轉,這些位元組應該怎麼轉。都不清楚。是以,在轉換的過程中,就有可能出錯。于是,就有了字元流,專門解決這個問題。

字元流 = 位元組流 + 編碼表;字元流的底層會用普通位元組流進行讀寫,然後字元流僅僅是做了把位元組與字元進行轉換的事情。這樣就不需要程式員自己來轉了。就不會出錯了!

編碼表

編碼表:可以看做是一個字典。這本字典翻譯是 人類的字元 和 機器語言(二進制) 之間對應關系。

編碼表:就是人類生活的字元和計算機二進制的對照關系表。

ASCII:最早的,美國人制定的一張碼表。包含字母、數字、符号、拉丁文。它用1個位元組的最低7位表示資料。

a 97 01100001

ISO8859-1:歐洲制定的碼表。相容ASCII。多了一些歐洲的語言。它用1個位元組的全部來表示資料。沒有未知字元的!

GB2312:識别數千中文

GBK:識别2萬多中文,2個位元組表示一個字元。

GB18030:GBK的更新

Unicode:世界計算機協會制定通用碼表,2個位元組表示一個字元。

UTF-8:Unicode更新版。能用1個位元組表示的就用1個位元組,要用兩個位元組的就用2個位元組,能用3個位元組表示的就用3個位元組。漢字基本都是3個位元組

編碼和解碼

     * 編碼:把我們能看懂的變成看不懂的(字元 --> 位元組)

     *     byte[] getBytes() 使用平台的預設字元集将此 String 編碼為 byte 序列

     *     byte[] getBytes(String charsetName)使用指定的字元集将此 String 編碼為 byte 序列

     * 解碼:把我們看不懂的變成能看懂的(位元組 --> 字元)

     *     String(byte[] bytes) 通過使用平台的預設字元集解碼指定的 byte 數組,構造一個新的 String

     *     String(byte[] bytes, String charsetName)通過使用指定的 charset 解碼指定的 byte 數組,構造一個新的 String

     * 亂碼:

     *     原因:編碼和解碼的碼表不一緻

public static void main(String[] args) throws UnsupportedEncodingException {
        encode();
	System.out.println("------------------");
	decode();
	System.out.println("------------------");
	luanCode();
}
// 示範:亂碼
public static void luanCode() throws UnsupportedEncodingException {
	String str = "你好";
	// 用UTF-8編碼
	byte[] bytes = str.getBytes("UTF-8");
	// 把這個數組給别人,讓他解碼,用GBK
	String s = new String(bytes,"GBK");
	System.out.println(s);// 浣犲ソ
		
	// 先用GBK編碼
	byte[] bytes2 = s.getBytes("GBK");
	// 在用UTF-8解碼
	String s2 = new String(bytes2,"UTF-8");
	System.out.println(s2);// 你好
}
// 示範:解碼
public static void decode() throws UnsupportedEncodingException {
	// 準備位元組數組
	byte[] gbk_bytes = {-60, -29, -70, -61};
	// String(byte[] bytes) 通過使用平台的預設字元集解碼指定的 byte 數組,構造一個新的 String
	String s1 = new String(gbk_bytes);
	System.out.println(s1);// 你好
	
	byte[] utf_bytes = {-28, -67, -96, -27, -91, -67};
        String s2 = new String(utf_bytes,"UTF-8");
	System.out.println(s2);
}
// 示範:編碼
public static void encode() throws UnsupportedEncodingException {
	String s = "你好";
	// 使用平台預設編碼集進行編碼
	byte[] bytes = s.getBytes();
	System.out.println(Arrays.toString(bytes));// [-60, -29, -70, -61]
	
	// 用GBK編碼
	byte[] bgk_bytes = s.getBytes("GBK");
	System.out.println(Arrays.toString(bgk_bytes));// [-60, -29, -70, -61]
	// 用UTF-8編碼
	byte[] utf_bytes = s.getBytes("UTF-8");
	System.out.println(Arrays.toString(utf_bytes));// [-28, -67, -96, -27, -91, -67]
}
           

運作結果:

IO流複習總結(三) 字元流、編碼表

字元流

Writer是寫入字元流的抽象類

OutputStreamWriter(輸出轉換流)是字元流通向位元組流的橋梁,将要寫入流的字元編碼成位元組。

構造方法摘要OutputStreamWriter(OutputStream out) 建立使用預設字元編碼的 OutputStreamWriter。OutputStreamWriter(OutputStream out, String charsetName) 建立使用指定字元集的 OutputStreamWriter。

字元流的底層還是用位元組流進行讀寫,字元流僅僅是做了位元組和字元的轉換(編碼和解碼)。接收一個位元組流,形成了字元流。把位元組流轉換為字元流!接收字元資料,首先要把字元資料變成位元組資料,然後再用普通位元組流,把資料寫到硬碟。實作了字元資料轉換為位元組資料。

Writer的5種寫出功能

     *     public void write(int c) 寫出一個字元

     *     public void write(char[] cbuf) 寫出字元數組

     *     public void write(char[] cbuf,int off,int len) 寫出字元數組cbuf中,從off開始,共len個字元

     *     public void write(String str) 寫出字元串

     *     public void write(String str,int off,int len)寫出字元串,從off開始,共len個字元

public static void main(String[] args) throws IOException {
	// 建立輸出流,關聯目标檔案
	OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("osw2.txt"));
	// 寫出資料
	// public void write(int c) 寫出一個字元
	osw.write('a');
	osw.write('你');
	osw.write('9');
	
	// public void write(char[] cbuf) 寫出字元數組
	char[] cbuf = {'h','e','l','l','o','柳','岩'};
	osw.write(cbuf);
	
	// public void write(char[] cbuf,int off,int len) 寫出字元數組cbuf中,從off開始,共len個字元
	// 需求:寫出:lo柳岩
	osw.write(cbuf, 3, 4);
	
	String str = "helloworld";
	// public void write(String str) 寫出字元串
	osw.write(str);
	// public void write(String str,int off,int len)寫出字元串,從off開始,共len個字元
	// 需求:寫出owo
	osw.write(str, 4, 3);
	// 釋放資源
	osw.close();
}
           

寫入結果:

IO流複習總結(三) 字元流、編碼表

Reader是用于讀取字元流的抽象類

InputStreamReader(輸入轉換流)是位元組流通向字元流的橋梁,将要讀取位元組并将其解碼成字元。

構造函數:InputStreamReader(InputStream in) 建立一個使用預設字元集的 InputStreamReaderInputStreamReader(InputStream in, String charsetName)建立使用指定字元集的 InputStreamReader。

字元流底層還是用的位元組流,字元流僅僅做字元和位元組的轉換!接收一個位元組流,生成的一個字元流。把位元組流轉換為字元流。底層會用位元組流去硬碟讀取位元組資料,把位元組資料轉換為字元資料,然後傳回。實作了把位元組資料轉換為字元資料。

字元流複制文本檔案

使用字元轉換流複制文本檔案,複制EncodeDecodeDemo.java 到 copy.java

public static void main(String[] args) throws IOException {
	// 建立輸入流,關聯源檔案
	InputStreamReader isr = new InputStreamReader(new FileInputStream("EncodeDecodeDemo.java"));
	// 建立輸出流,關聯目标檔案
	OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("copy.java"));
	// 讀寫資料
	// 一次一個字元
//	int c = 0;
//	while((c = isr.read()) != -1){
//		osw.write(c);
//	}
	// 一次多個字元
	char[] cbuf = new char[1024];
	int len = 0;
	while((len = isr.read(cbuf)) != -1){
		osw.write(cbuf, 0, len);
	}
	// 釋放資源
	isr.close();
	osw.close();
}
           

轉換流的便捷形式:FileWriter和FileReader

/*
 * 示範:FileWriter的使用
 * 	public FileWriter(String pathname)
 * 	public FileWriter(File file)
 * 	public FileWriter(String pathname,boolean append)
 * 	public FileWriter(File file,boolean append)
 */
public class FileWriterDemo01 {
	public static void main(String[] args) throws IOException {
		// 建立輸出流,關聯目标檔案
		FileWriter fw = new FileWriter("fw.txt");
		// 寫出資料
		fw.write("hello,虎哥");
		// 釋放資源
		fw.close();
	}
}
           
/*
 * 示範:FileReader便捷類的使用
 * 	public FileReader (String pathname)
 * 	public FileReader (File file)
 */
public class FileReaderDemo01 {
	public static void main(String[] args) throws IOException {
		// 建立輸入流,關聯源檔案
		FileReader fr = new FileReader("copy.java");
		// 讀取資料
		char[] cbuf = new char[1024];
		int len = 0;
		while((len = fr.read(cbuf)) != -1){
			System.out.print(new String(cbuf,0,len));
		}
		// 釋放資源
		fr.close();
	}
}
           

便捷類複制檔案

public class CopyFileDemo {
	public static void main(String[] args) throws IOException {
		// 建立輸入流,關聯源檔案
		FileReader fr = new FileReader("copy.java");
		// 建立輸出流,關聯目标檔案
		FileWriter fw = new FileWriter("copy2.java");
		// 讀寫資料
		char[] cbuf = new char[1024];
		int len = 0 ;
		while((len = fr.read(cbuf)) != -1){
			fw.write(cbuf, 0, len);
		}
		// 釋放資源
		fr.close();
		fw.close();
	}
}
           

便捷類和父類的關系

父類:

    OutputStreamWriter = OutputStream + 任意的編碼表

    InputStreamReader = InputStream + 任意編碼表

子類:

    FileWriter = FileOutputStream + 本地預設編碼表

    FileReader = FileInputStream + 本地預設編碼表

字元緩沖流

為了高效的進行讀寫,Java提供了字元的緩沖流,也就是在緩沖流的内部維護了緩沖數組,這樣以來就能實作字元資料的高效讀寫!普通字元流,内部有一個緩沖數組,這是一個位元組數組。作用是減少了流與硬碟之間的互動次數。提高了效率。緩沖字元流,内部有一個緩沖數組,這是一個字元數組。有什麼作用?

普通字元流,每次write都會調用一次編碼轉換器,調用編碼轉換器,本身也是非常消耗資源的!

緩沖字元流,當我們需要write時,不會去調用編碼轉換器,而是把要寫的字元存入緩沖區,等緩沖區中足夠多,然後一次性調用編碼轉換器,把資料轉為位元組。這樣就減少了調用編碼轉換器的次數,進而提高了效率。

BufferedReader的特有功能:String readLine()讀取一個文本行

// 普通方法複制
public static void method_01() throws IOException {
	// 建立輸入流,關聯源檔案
	BufferedReader br = new BufferedReader(new FileReader("copy.java"));
	// 建立輸出流,關聯目标檔案
	BufferedWriter bw = new BufferedWriter(new FileWriter("copy3.java"));
	// 讀寫資料
	char[] cbuf = new char[1024];
	int len = 0;
	while ((len = br.read(cbuf)) != -1) {
		bw.write(cbuf, 0, len);
		bw.flush();
	}
	// 釋放資源
	br.close();
	bw.close();
}
           
// 特有功能複制
public static void method_02() throws IOException {
	// 建立輸入流,關聯源檔案
	BufferedReader br = new BufferedReader(new FileReader("copy.java"));
	// 建立輸出流,關聯目标檔案
	BufferedWriter bw = new BufferedWriter(new FileWriter("copy4.java"));
	// 讀寫資料
	String line = null;
	while((line = br.readLine()) != null){
		bw.write(line);
		bw.newLine();
		bw.flush();
	}
	// 釋放資源
	br.close();
	bw.close();
}
           

IO流的總結

1、IO流的繼承體系

InputStream:位元組輸入流:FileInputStream、BufferedInputStream

OutputStream:位元組輸出流FileOutputStream、BufferedOutputStream

    Reader:字元輸入流InputStreamReader、FileReader、BufferedReader

Writer:字元輸出流OutputStreamWriter、FileWriter、BufferedWriter

2、厘清構造函數

1)四大基本流,他們都是直接關聯檔案或路徑名FileInputStream、FileOutputStream、FileReader、FileWriter

2)裝飾流,他們的構造函數接收本體系的頂級父類BufferedInputStream、BufferedOutputStream、BufferedReader、BufferedWriter

3)轉換流,構造函數,接收對應的位元組流頂級父類。InputStreamReader、OutputStreamWriter

3、如何選擇具體的流?

原則:先選擇基本流。

問題1:我是位元組資料還是字元資料

位元組資料:FileInputStream\FileOutputStream

字元資料:FileReader\FileWriter

問題2:我是讀還是寫?

讀:FileInputStream\FileReader

寫:FileOutputStream\FileWriter

問題3:我是否需要高效緩沖呢?

是:我就用選擇的這個流對應體系的緩沖流包起來。

問題4:如果需要指定編碼表,那麼可以考慮用父類替換對應的便捷類。