Java IO之字符流
1、字符集與字符編碼
- 爲何要有字符集
咱們在計算機屏幕上看到的是實體化的文字,而在計算機存儲介質中存放的實際是二進制的比特流。那 麼在這二者之間的轉換規則就須要一個統一的標準,不然就會出現亂碼了現象;小夥伴QQ上傳過來的文件,在咱們本地打開又亂碼了。 因而爲了實現轉換標準,各類字符集標準就出現了。html
- 什麼是字符集
簡單的說字符集就規定了某個文字對應的二進制數字存放方式(編碼)和某串二進制數值表明了哪一個文字(解 碼)的轉換關係。java
- 什麼是字符編碼
計算機只能存儲0,1之類2進制數字,怎麼樣讓它表示那麼多各類各樣的字符呢?就須要對各類字符指定一個數值的編碼代號,這就是字符編碼。如:a這個字符,在ascii字符集編碼表中對應的編號是97,而「中」在gb2312字符集中對應的編號是:16進制是D6D0 10進制是54992 。經過編號就能夠找到計算機對應字符。不用將那麼複雜的字符保存在計算機中,只須要保存它代號就好。字符集只是指定了一個集合中有哪些字符,而字符編碼,是爲這個集合中全部字符定義個編號,這就是字符集與編碼區別所在。編程
此外計算機中只保存字符在某字符集中對應的字符編號值,計算機只須要維持一份字符集清單,當讀到這種編號值(編碼),就在對應字符清單中找出該字符顯示出來便可。字符大小顏色都是程序按照字符樣式繪製而成的。如圖:數組
計算機並不區分二進制文件與文本文件。全部的文件都是以二進制形式來存儲的,所以,從本質上說,全部的文件都是二進制文件。因此字符流是創建在字節流之上的,它可以提供字符層次的編碼和解碼。列如,在寫入一個字符時,Java虛擬機會將字符轉爲文件指定的編碼(默認是系統默認編碼),在讀取字符時,再將文件指定的編碼轉化爲字符。app
常見的碼錶以下:編程語言
ASCII:美國標準信息交換碼。用一個字節的7位能夠表示。ide
ISO8859-1:拉丁碼錶。歐洲碼錶,用一個字節的8位表示。又稱Latin-1(拉丁編碼)或「西歐語言」。ASCII碼是包含的僅僅是英文字母,而且沒有徹底佔滿256個編碼位置,因此它以ASCII爲基礎,在空置的0xA0-0xFF的範圍內,加入192個字母及符號藉以供使用變音符號的拉丁字母語言使用。從而支持德文,法文等。於是它依然是一個單字節編碼,只是比ASCII更全面。測試
GB2312:英文佔一個字節,中文佔兩個字節.中國的中文編碼表。ui
GBK:中國的中文編碼表升級,融合了更多的中文文字符號。編碼
Unicode: 國際標準碼規範,融合了多種文字。全部文字都用兩個字節來表示,Java語言使用的就是unicode。
UTF-8:最多用三個字節來表示一個字符。
咱們之後接觸最多的是iso8859-一、gbk、utf-8
查看上述碼錶後,很顯然中文的‘中’在iso8859-1中是沒有對映的編碼的。或者一個字符在2中碼錶中對應的編碼不一樣,例若有一些字在不一樣的編碼中是有交集的,例如bjg5 和gbk 中的漢字簡體和繁體多是同樣的,就是有交集,可是在各自碼錶中的數字不同。
例如使用gbk 將中文保存在計算機中,中國對應gbk編碼爲100 200若是使用big5 打開,可能 ? ... 不一樣的編碼對映的是不同的。很顯然,咱們使用什麼樣的編碼寫數據,就須要使用什麼樣的編碼來讀數據。
ISO8859-1:一個字節
GBK: 兩個字節包含了英文字符和擴展的中文 ISO8859-1+中文字符
UTF-8 萬國碼,推行的。是1~3個字節不等長。英文存的是1個字節,中文存的是3個字節,是爲了節省空間。
- 亂碼
若是一個字符按照一個字符集存儲在計算機中(編碼),而使用另外一種字符集讀取該字符(解碼)那就會出現亂碼現象。應爲計算機中存儲使用的字碼表與讀取使用的字碼表不匹配。
2、java中字符的編碼與解碼
計算機中存儲和傳輸的基本單位是字節,而咱們看到的信息是字符(人能看懂的)。
編碼:編碼就是真實字符與二進制串的對應關係,真實字符轉換成二進制串。編碼是信息從一種形式或格式轉換爲另外一種形式的過程也稱爲計算機編程語言的代碼簡稱編碼。用預先規定的方法將文字、數字或其它對象編成數碼,或將信息、數據轉換成規定的電脈衝信號。
java中的編碼:
在byte[] buffer=string.getBytes();中,若是沒有給.getBytes();指定字符集,那麼在編碼過程當中,就會按照系統默認的編碼格式進行編碼。
String str="中"; byte [] b_gbk=str.getBytes("GBK"); byte [] b_utf=str.getBytes("UTF-8"); byte [] b_unic=str.getBytes("UTF-16"); byte [] b_Iso=str.getBytes("ISO8859-1");
解碼:解碼就是二進制串與真實字符的對應關係,二進制串轉換成真實字符。將信息從已經編碼的形式恢復到編碼前原狀的過程。也就是用特定方法把數碼還原成它所表明的內容或將電脈衝信號、光信號、無線電波等轉換成它所表明的信息、數據等的過程。
java中的解碼:
//str按照 GBK編碼 後 ,再按照GBK解碼 String str_1=new String(str.getBytes("GBK"), "GBK"); String str_2=new String(str.getBytes("UTF-8"), "UTF-8"); String str_3=new String(str.getBytes("UTF-32"), "UTF-32"); String str_4=new String(str.getBytes("ISO8859-1"), "ISO8859-1");
3、關於字符流的一些探討:
字符流爲什麼存在
既然字節流提供了可以處理任何類型的輸入/輸出操做的功能,那爲何還要存在字符流呢?容我慢慢道來,字節流不能直接操做Unicode字符,由於一個字符有兩個字節,字節流一次只能操做一個字節。若是JAVA不能直接操做字符,我會感到JAVA對這個世界滿滿的惡意,因此提供對直接的字符輸入/輸出的支持是頗有必要的,由於咱們的口號是:一次編寫,處處運行。
字符流的概念
輸出字符流:把要寫入文件的字符序列(實際是unicode碼元序列)轉爲指定編碼方式下的字節序列,而後在寫入文件中。
輸入字符流:把要讀取的字節序列按照指定編碼方式轉爲相應的字符序列(實際是unicode碼元序列),從而寫入內存中。
字符流的層次關係
字符流層次結構的頂層是Reader和Writer抽象類,與字節流中的InputStream、OutputStream相對應。經常使用類繼承結構圖以下:
4、字符流
字符流處理的單元爲2個字節的Unicode字符,分別操做字符、字符數組或字符串。字符流是由Java虛擬機將字節轉化爲2個字節的Unicode字符爲單位的字符而成的。字符流操做的是緩衝區(當咱們對文件進行讀寫操做時若是不調用close() 或 flush()方法時不能看到數據的變化)。
java的文本(char)是16位無符號整數,是字符的unicode編碼(雙字節編碼),文件是byte byte byte ...的數據序列,文本文件是文本(char)序列按照某種編碼方案(utf-8,utf-16be,gbk)序列化爲byte的存儲結果
字符流(Reader Writer)---->操做的是文本文本文件,字符的處理,一次處理一個字符,字符的底層任然是基本的字節序列
(一)、字符輸入流
將磁盤(文件)中的數據讀入內存中
Reader 是全部的輸入字符流的父類,它是一個抽象類。
InputStreamReader:將字節輸入流轉換爲字符輸入流。是字節流通向字符流的橋樑,能夠指定字節流轉換爲字符流的字符集。完成byte流解析爲char流,按照編碼解析
FileReader:該類從InputStreamReader類繼承而來,能夠關聯源文件,是字符流的過濾器。
BufferedReader(緩衝流):字節輸入緩衝流,一次只能讀一行,從字符輸入流中讀取文本,緩衝各個字符,從而實現字符、數組和行的高效讀取。BufferedReader 由Reader類擴展而來,提供通用的緩衝方式文本讀取,並且提供了很實用的readLine。
LineNumberReader(關於行號緩衝流):它是BufferedReader的子類,LineNumberReader比BufferedReader多了個功能,就是能夠獲取當前行號getLineNumber()與設置起始行號setLineNumber(int lineNumber)。默認狀況下,行編號從 0 開始。該行號隨數據讀取在每一個行結束符處遞增,而且能夠經過調用 setLineNumber(int)
更改行號。但要注意是,setLineNumber(int)
不會實際更改流中的當前位置;它只更改將由 getLineNumber() 返回的值。
(二)、字符輸出流
將內存中的數據按照字符形式寫入磁盤(文件)中
Writer:是全部的輸出字符流的父類,它是一個抽象類。
OutputStreamWriter:將字節輸出流轉爲字符輸出流,是字符流通向字節流的橋樑,可以使用指定的 charset 將要寫入流中的字符編碼成字節。它使用的字符集能夠由名稱指定或顯式給定,不然將接受平臺默認的字符集。即OutputStreamWriter提供char流到byte流,按照編碼處理
FileWriter:類從OutputStreamWriter類繼承而來。該類按字符向流中寫入數據,能夠關聯源文件,是字符流的過濾器
BufferedWriter(緩衝流):字節輸出緩衝流,將文本寫入字符輸出流,一次只能寫一行,緩衝各個字符,從而提供單個字符、數組和字符串的高效寫入。 能夠指定緩衝區的大小,或者接受默認的大小。在大多數狀況下,默認值就足夠大了。 該類提供了 newLine() 方法,它使用平臺本身的行分隔符概念,此概念由系統屬性 line.separator 定義。該類寫入文本的方法就是父類Writer提供的write()系列方法,在後面的示例中我麼將演示他特有的newLine( )方法。
5、示例分析
(一)、InputStreamReader和OutStreamWriter示例
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
1 package me.io.chars; 2 3 import java.io.FileInputStream; 4 import java.io.FileOutputStream; 5 import java.io.IOException; 6 import java.io.InputStreamReader; 7 import java.io.OutputStreamWriter; 8 9 /** 10 * InputStreamReader和OutStreamWriter示例 11 * 12 * @author Administrator 13 * 14 */ 15 public class IsrAndOswDemo { 16 public static void main(String[] args) throws IOException { 17 String file = "src/me/io/IOUtils.java"; 18 //默認項目的編碼,未來操做時要寫文件自己的編碼格式,否則會亂碼 19 InputStreamReader isr = new InputStreamReader(new FileInputStream(file)); 20 OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("test.java"), "UTF-8"); 21 /* 22 int num = 0; 23 while((num = isr.read()) != -1) { 24 System.out.print((char)num); 25 }*/ 26 int c = 0; 27 char[] buf = new char[8*1024]; 28 StringBuilder sb = new StringBuilder(); 29 /** 30 * 批量讀取,放入到buf這個字符數組,從第零個位置開始放,最多放buf.length個 31 * 返回的是讀取到的字符個數 32 */ 33 while((c = isr.read(buf, 0, buf.length)) != -1) { 34 sb.append(buf, 0, c); 35 osw.write(buf, 0, c); 36 osw.flush(); 37 } 38 System.out.print(sb); 39 isr.close(); 40 osw.close(); 41 } 42 }
(二)、FileReader和FileWriter示例
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
1 package me.io.chars; 2 3 import java.io.FileReader; 4 import java.io.FileWriter; 5 import java.io.IOException; 6 7 /** 8 * FileReader和FileWriter測試類 9 * @author Administrator 10 * 11 */ 12 public class FrAndFwDemo { 13 public static void main(String[] args) throws IOException { 14 15 FileReader fr = new FileReader("src/me/io/IOUtils.java"); 16 //參數true表明能夠追加內容 17 FileWriter fw = new FileWriter("demo/test.java", true); 18 char[] buf = new char[8*1024]; 19 int c = 0; 20 while((c = fr.read(buf, 0, buf.length)) != -1) { 21 fw.write(buf, 0, c); 22 fw.flush(); 23 } 24 fr.close(); 25 fw.close(); 26 } 27 }
(三)、BufferedReader,BufferedWriter,PrintWriter示例
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
1 package me.io.chars; 2 3 import java.io.BufferedReader; 4 import java.io.BufferedWriter; 5 import java.io.FileInputStream; 6 import java.io.FileOutputStream; 7 import java.io.IOException; 8 import java.io.InputStreamReader; 9 import java.io.OutputStreamWriter; 10 import java.io.PrintWriter; 11 12 /** 13 * 過濾流 14 * BufferedReader,BufferedWriter,PrintWriter示例 15 * @author Administrator 16 * 17 */ 18 public class BrAndBwOrPwDemo { 19 20 public static void main(String[] args) throws IOException { 21 BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("src/me/io/IOUtils.java"))); 22 //BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("demo/test.java"))); 23 PrintWriter pw = new PrintWriter("demo/test.java"); 24 String line = null; 25 while((line = br.readLine()) != null) { 26 //不能讀取換行 27 System.out.println(line); 28 // bw.write(line); 29 // //單獨寫出換行操做 30 // bw.newLine(); 31 // bw.flush(); 32 pw.println(line); 33 pw.flush(); 34 } 35 br.close(); 36 //bw.close(); 37 pw.close(); 38 } 39 }
參考文章:
https://www.cnblogs.com/jalja/p/6030137.html