J07-Java IO流總結七 《 InputStreamReader和OutputStreamWriter 》
前面在介紹FileReader和FileWriter的時候有說到,FileReader的讀取字符功能,以及FileWriter的寫出字符的功能,都不是它們自己實現的,而是,它們分別繼承了InputStreamReader和OuputStreamWriter這兩個轉換流,利用這兩個轉換流,實現了字節數據與字符數據之間的轉換,關於這點可以通過FileReader和FileWriter的源碼看出來。
下面將介紹這兩個轉換流,並分別通過幾個簡單的應用場景來熟悉它們的用法。
1. InputStreamReader
1.1 概念介紹
InputStreamReader將底層的字節數據轉換為字符數據
根據API文檔的描述,InputStreamReader 是字節流通向字符流的橋梁,它使用指定的 charset 讀取字節並將其解碼為字符。它使用的字符集可以由名稱指定或顯式給定,或者可以接受平臺默認的字符集。
每次調用 InputStreamReader 中的一個 read() 方法都會導致從底層輸入流讀取一個或多個字節。要啟用從字節到字符的有效轉換,可以提前從底層流讀取更多的字節,使其超過滿足當前讀取操作所需的字節。
為了達到最高效率,可要考慮在 BufferedReader 內包裝 InputStreamReader。例如:
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
關於源碼:
InputStreamReader的源碼比較簡單,它有一個非常重要的成員變量:
private final StreamDecoder sd;
InputStreamReader的主要實現都調用了這個流解碼類StreamDecoder,它只有這一個成員變量,而這一個類把它的所有操作都包括了。StreamDecoder的主要作用是獲取一個字節輸入流InputStream,然後根據給定的編碼方案讀取字符,如果沒有給定,則默認是系統編碼。
由源碼可知,InputStreamReader的設計使用到了裝飾設計模式,它本身並沒有提供啥功能,具體的實現其實都由StreamDecoder來實現了。
源碼中關於InputStreamReader的幾個構造方法需要格外註意一下,因為通過它的構造方法,可以顯式的指定解碼所需的字符集:
1.2 應用示例
System.in是字節流對象,代表鍵盤的輸入,如果我們想按行接收用戶的輸入時,就必須用到緩沖字符流BufferedReader特有的方法readLine(),但是經過觀察會發現在創建BufferedReader的構造方法的參數必須是一個Reader對象,如下:
public BufferedReader(Reader in)
這時候我們的轉換流InputStreamReader就派上用場了。
示例代碼如下所示:
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; public class InputStreamReaderTest { public static void main(String[] args) { BufferedReader br = null; String str = null; try { br = new BufferedReader(new InputStreamReader(System.in)); while(!"exit".equals(str = br.readLine())) { System.out.println(str); } } catch (IOException e) { e.printStackTrace(); } } }
代碼運行效果:
2. OutputStreamWriter
2.1 概念介紹
OutputStreamWriter是將字符數據轉換為字節數據的流。可以顯式的或者使用平臺默認的字符集進行轉換。所有的字符輸出流的轉換工作都依賴於它來完成。
根據API文檔的描述,OutputStreamWriter 是字符流通向字節流的橋梁:可使用指定的 charset 將要寫入流中的字符編碼成字節。它使用的字符集可以由名稱指定或顯式給定,否則將接受平臺默認的字符集。
每次調用 write() 方法都會導致在給定字符(或字符集)上調用編碼轉換器。在寫入底層輸出流之前,得到的這些字節將在緩沖區中累積。可以指定此緩沖區的大小,不過,默認的緩沖區對多數用途來說已足夠大。註意,傳遞給 write() 方法的字符沒有緩沖。
為了獲得最高效率,可考慮將 OutputStreamWriter 包裝到 BufferedWriter 中,以避免頻繁調用轉換器。例如:
Writer out = new BufferedWriter(new OutputStreamWriter(System.out));
源碼:
閱讀OutputStreamWriter的源碼,可以發現它跟InputStreamReader一樣使用了裝飾器模式,在它的類當中,持有了一個的引用StreamEncoder的應用,StreamEncoder可以理解為一個編碼器,當調用OutputStreamWriter的write、close方法時,實際上底層調用的還是這個StreamEncoder的方法。
需要留意一下OutputStreamWriter的構造方法:
2.2 應用示例
System.out是字節流對象,代表輸出到顯示器,在上面的示例中按行讀取用戶的輸入後,並且要將讀取的一行字符串直接顯示到控制臺,就需要用到字符流的write(String str)方法,所以我們要使用OutputStreamWriter將字符流轉化為字節流。
示例代碼如下所示:
import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; public class ConvertStreamTest { public static void main(String[] args) { BufferedReader br = null; BufferedWriter bw = null; String str = null; try { br = new BufferedReader(new InputStreamReader(System.in)); bw = new BufferedWriter(new OutputStreamWriter(System.out)); while(!"bye".equals(str = br.readLine())) { bw.write("-->" + str); bw.newLine(); bw.flush(); } } catch (IOException e) { e.printStackTrace(); } } }
代碼運行效果:
3 轉換流綜合應用示例
下面的示例代碼中定義了兩個方法,分別為Utf8ToGbk()和gbkToUtf8(),其中,Utf8ToGbk()實現了從一個用UTF-8編碼的源文件復制數據到一個用GBK編碼的目標文件,gbkToUtf8()方法則正好反之。
示例代碼:
import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; public class ConvertTest { public static void main(String[] args) { // gbkToUtf8(); Utf8ToGbk(); } //////////////////////////////////////////////////////////// private static void Utf8ToGbk() { BufferedReader br = null; BufferedWriter bw = null; try { br = new BufferedReader(new InputStreamReader(new FileInputStream("./src/res/2_copy.txt"), "utf-8")); bw = new BufferedWriter(new FileWriter("./src/res/2.txt")); // bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("./src/res/2.txt"))); String str = null; while(null != (str = br.readLine())) { bw.write(str); bw.newLine(); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if(null != bw) { try { bw.close(); } catch (IOException e) { e.printStackTrace(); } } if(null != br) { try { br.close(); } catch (IOException e) { e.printStackTrace(); } } } } //////////////////////////////////////////////////////////// private static void gbkToUtf8() { BufferedReader br = null; BufferedWriter bw = null; try { br = new BufferedReader(new FileReader("./src/res/2.txt")); // br = new BufferedReader(new InputStreamReader(new FileInputStream("./src/res/2.txt")));//使用默認字符集 bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("./src/res/2_copy.txt"), "utf-8")); String str = null; while(null != (str = br.readLine())) { bw.write(str); bw.newLine(); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if(null != bw) { try { bw.close(); } catch (IOException e) { e.printStackTrace(); } } if(null != br) { try { br.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
J07-Java IO流總結七 《 InputStreamReader和OutputStreamWriter 》