字符是我們能讀懂的一些文字和符號,但在計算機中存儲的卻是我們看不懂的byte 字節,那這就存在關於字符編碼解碼的問題。所以在學習Io流的字符流前我們先了解些關於編碼問題。
一、字符集與字符編碼
1、什么要有字符集
我們在計算機屏幕上看到的是實體化的文字,而在計算機存儲介質中存放的實際是二進制的比特流。那 么在這兩者之間的轉換規則就需要一個統一的標准,否則就會出現亂碼了現象;小伙伴QQ上傳過來的文件,在我們本地打開又亂碼了。 於是為了實現轉換標准,各種字符集標准就出現了。
2、什么是字符集
簡單的說字符集就規定了某個文字對應的二進制數字存放方式(編碼)和某串二進制數值代表了哪個文字(解 碼)的轉換關系。
3、什么是字符編碼
字符編碼:計算機只能存儲0,1之類2進制數字,怎么樣讓它表示那么多各種各樣的字符呢?就需要對各種字符指定一個數值的編碼代號,這就是字符編碼。如:a這個字符,在ascii字符集編碼表中對應的編號是97,而“中”在gb2312字符集中對應的編號是:16進制是D6D0 10進制是54992 。通過編號就可以找到計算機對應字符。不用將那么復雜的字符保存在計算機中,只需要保存它代號就好。字符集只是指定了一個集合中有哪些字符,而字符編碼,是為這個集合中所有字符定義個編號,這就是字符集與編碼區別所在。
計算機中只保存字符在某字符集中對應的字符編號值,計算機只需要維持一份字符集清單,當讀到這種編號值(編碼),就在對應字符清單中找出該字符顯示出來即可。字符大小顏色都是程序按照字符樣式繪制而成的。如圖:
4、亂碼
如果一個字符按照一個字符集存儲在計算機中(編碼),而使用另一種字符集讀取該字符(解碼)那就會出現亂碼現象。應為計算機中存儲使用的字碼表與讀取使用的字碼表不匹配。
二、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");
三、字符流
字符流:字符流處理的單元為2個字節的Unicode字符,分別操作字符、字符數組或字符串。字符流是由Java虛擬機將字節轉化為2個字節的Unicode字符為單位的字符而成的。字符流操作的是緩沖區(當我們對文件進行讀寫操作時如果不調用close() 或 flush()方法時不能看到數據的變化)。
(一)、字符輸入流
將磁盤(文件)中的數據讀入內存中
Reader 是所有的輸入字符流的父類,它是一個抽象類。
public abstract class Reader implements Readable, Closeable { protected Object lock; protected Reader() { this.lock = this; } protected Reader(Object lock) { if (lock == null) { throw new NullPointerException(); } this.lock = lock; } //試圖將字符讀入指定的字符緩沖區。緩沖區可照原樣用作字符的存儲庫:所做的唯一改變是 put 操作的結果。不對緩沖區執行翻轉或重繞操作。 public int read(java.nio.CharBuffer target) throws IOException {} //讀取單個字符。在字符可用、發生 I/O 錯誤或者已到達流的末尾前,此方法一直阻塞。 用於支持高效的單字符輸入的子類應重寫此方法。 public int read() throws IOException { } //將字符讀入數組。在某個輸入可用、發生 I/O 錯誤或者已到達流的末尾前,此方法一直阻塞。 public int read(char cbuf[]) throws IOException {} // 將字符讀入數組的某一部分。在某個輸入可用、發生 I/O 錯誤或者到達流的末尾前,此方法一直阻塞。 abstract public int read(char cbuf[], int off, int len) throws IOException; //跳過字符。在某個字符可用、發生 I/O 錯誤或者已到達流的末尾前,此方法一直阻塞。 public long skip(long n) throws IOException {} //判斷是否准備讀取此流。 public boolean ready() throws IOException { } //判斷此流是否支持 mark() 操作。默認實現始終返回 false。子類應重寫此方法。 public boolean markSupported() {} //標記流中的當前位置。對 reset() 的后續調用將嘗試將該流重新定位到此點。並不是所有的字符輸入流都支持 mark() 操作。 public void mark(int readAheadLimit) throws IOException { } //重置該流。如果已標記該流,則嘗試在該標記處重新定位該流。如果已標記該流,則以適用於特定流的某種方式嘗試重置該流, //例如,通過將該流重新定位到其起始點。並不是所有的字符輸入流都支持 reset() 操作,有些支持 reset() 而不支持 mark()。 public void reset() throws IOException { } //關閉該流並釋放與之關聯的所有資源。在關閉該流后,再調用 read()、ready()、mark()、reset() 或 skip() 將拋出 IOException。關閉以前關閉的流無效。 abstract public void close() throws IOException; }
InputStreamReader:將字節輸入流轉換為字符輸入流。是字節流通向字符流的橋梁,可以指定字節流轉換為字符流的字符集。夠造方法如下:
//InputStreamReader(InputStream in) 創建一個使用默認字符集的 InputStreamReader。 // InputStreamReader(InputStream in, String charsetName) 創建使用指定字符集的 InputStreamReader。
InputStreamReader 讀取文件內容的三種方式
//public int read() throws IOException //讀取單個字符。在字符可用、發生 I/O 錯誤或者已到達流的末尾前,此方法一直阻塞。 public static void readOneStr() throws IOException{ InputStreamReader isr=new InputStreamReader(new FileInputStream("E:\\test\\javaIo\\1.txt")); int ch=0; while((ch=isr.read())!=-1){ System.out.print((char)ch); } isr.close(); } //public int read(char[] cbuf) throws IOException //將字符讀入數組。在某個輸入可用、發生 I/O 錯誤或者已到達流的末尾前,此方法一直阻塞。 public static void readOneStrArr() throws IOException{ InputStreamReader isr=new InputStreamReader(new FileInputStream("E:\\test\\javaIo\\1.txt")); char [] ch=new char[1024]; int len=0; while((len=isr.read(ch))!=-1){ System.out.print(new String(ch,0,len)); } isr.close(); } //public int read(char[] cbuf) throws IOException //將字符讀入數組的某一部分。在某個輸入可用、發生 I/O 錯誤或者到達流的末尾前,此方法一直阻塞。 public static void readOneStrArrLen() throws IOException{ InputStreamReader isr=new InputStreamReader(new FileInputStream("E:\\test\\javaIo\\1.txt")); char [] ch=new char[1024]; int len=0; while((len=isr.read(ch,0,ch.length))!=-1){ System.out.print(new String(ch,0,len)); } isr.close(); }
FileReader:該類從InputStreamReader類繼承而來,可以關聯源文件。
1、構造方法
FileReader fr = new FileReader(String fileName);//使用帶有指定文件的String參數的構造方法。創建該輸入流對象。並關聯源文件。
該類讀取文件方式與InputStreamReader基本一樣,使用該類代碼較簡潔建議使用 ,例如:
public static void main(String[] args) throws IOException { FileReader fr=new FileReader("E:\\test\\javaIo\\1.txt"); char[] ch=new char[1024]; int len=0; while((len=fr.read(ch))!=-1){ System.out.println(new String(ch,0,len)); } fr.close(); }
BufferedReader(緩沖流):字節輸入緩沖流,從字符輸入流中讀取文本,緩沖各個字符,從而實現字符、數組和行的高效讀取。BufferedReader 由Reader類擴展而來,提供通用的緩沖方式文本讀取,而且提供了很實用的readLine。
該類讀取文本的方法就是父類Reader提供的read()系列方法。在這里演示他自己特有的方法readLine()
//public String readLine()throws IOException讀取一個文本行。通過下列字符之一即可認為某行已終止:換行 ('\n')、回車 ('\r') 或回車后直接跟着換行。 public static void readLineTest() throws IOException{ BufferedReader br=new BufferedReader(new FileReader("E:\\test\\javaIo\\1.txt")); String str=null; while((str=br.readLine())!=null){ System.out.println(str); } br.close(); }
LineNumberReader(關於行號緩沖流):它是BufferedReader的子類,LineNumberReader比BufferedReader多了個功能,就是可以獲取當前行號 getLineNumber()與設置起始行號 setLineNumber(int lineNumber)。默認情況下,行編號從 0 開始。該行號隨數據讀取在每個行結束符處遞增,並且可以通過調用 setLineNumber(int)
更改行號。但要注意的是,setLineNumber(int)
不會實際更改流中的當前位置;它只更改將由 getLineNumber() 返回的值。
public static void main(String[] args) throws IOException { LineNumberReader lnr=new LineNumberReader(new FileReader("E:\\test\\javaIo\\2.txt")); //public void setLineNumber(int lineNumber)設置當前行號。 如果不設置該值 行號默認從0開始 lnr.setLineNumber(10); String str=null; while((str=lnr.readLine())!=null){ //public int getLineNumber()獲得當前行號。 System.out.println(lnr.getLineNumber()+":"+str); } lnr.close(); }
(二)、字符輸出流
將內存中的數據按照字符形式寫入磁盤(文件)中
Writer:是所有的輸出字符流的父類,它是一個抽象類。該抽象類的方法如下:
public abstract class Writer implements Appendable, Closeable, Flushable { private char[] writeBuffer; private final int writeBufferSize = 1024; protected Object lock; protected Writer() { this.lock = this; } protected Writer(Object lock) { } // 寫入單個字符。要寫入的字符包含在給定整數值的 16 個低位中,16 高位被忽略。 用於支持高效單字符輸出的子類應重寫此方法。 public void write(int c) throws IOException { } //寫入字符數組。 public void write(char cbuf[]) throws IOException { } //寫入字符數組的某一部分。 abstract public void write(char cbuf[], int off, int len) throws IOException; //寫入字符串 public void write(String str) throws IOException { } //寫入字符串的某一部分。 public void write(String str, int off, int len) throws IOException { } //將指定字符序列添加到此 writer。 public Writer append(CharSequence csq) throws IOException { } //將指定字符序列的子序列添加到此 writer.Appendable。 public Writer append(CharSequence csq, int start, int end) throws IOException { CharSequence cs = (csq == null ? "null" : csq); write(cs.subSequence(start, end).toString()); return this; } //將指定字符添加到此 writer public Writer append(char c) throws IOException { write(c); return this; } //刷新該流的緩沖。如果該流已保存緩沖區中各種 write() 方法的所有字符,則立即將它們寫入預期目標。 //然后,如果該目標是另一個字符或字節流,則將其刷新。因此,一次 flush() 調用將刷新 Writer 和 OutputStream 鏈中的所有緩沖區。 abstract public void flush() throws IOException; //關閉此流,但要先刷新它。在關閉該流之后,再調用 write() 或 flush() 將導致拋出 IOException。關閉以前關閉的流無效 abstract public void close() throws IOException; }
OutputStreamWriter:將字節輸出流轉為字符輸出流,是字符流通向字節流的橋梁,可使用指定的 charset 將要寫入流中的字符編碼成字節。它使用的字符集可以由名稱指定或顯式給定,否則將接受平台默認的字符集。常用夠造方法如下:
//public OutputStreamWriter(OutputStream out, String charsetName); 可以設置編碼格式 //public OutputStreamWriter(OutputStream out)創建使用默認字符編碼的 OutputStreamWriter。
OutputStreamWriter將內存中的數據寫入文件的5中方式如下:
//public void write(int c)throws IOException //寫入單個字符。要寫入的字符包含在給定整數值的 16 個低位中,16 高位被忽略。 public static void writerOneChar() throws IOException{ OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("E:\\test\\javaIo\\1.txt")); osw.write(97);//char ch=(char)97; osw.write('c'); osw.flush(); osw.close(); } //public void write(char[] cbuf)throws IOException 寫入字符數組。 public static void writerCharArr() throws IOException{ OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("E:\\test\\javaIo\\1.txt")); char [] ch=new char[]{'我','愛','中','國'}; osw.write(ch); osw.flush(); osw.close(); } //public abstract void write(char[] cbuf,int off,int len)throws IOException 寫入字符數組的某一部分。 public static void writerCharArrLen() throws IOException{ OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("E:\\test\\javaIo\\1.txt")); char [] ch=new char[]{'我','愛','中','國'}; osw.write(ch,0,ch.length-1); osw.flush(); osw.close(); } //public void write(String str) throws IOException 寫入字符串。 public static void writerOneStr() throws IOException{ OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("E:\\test\\javaIo\\1.txt")); osw.write("中國"); osw.flush(); osw.close(); } //public void write(String str,int off, int len)throws IOException; 寫入字符串的某一部分。 public static void writerOneStrArrLen() throws IOException{ OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("E:\\test\\javaIo\\1.txt")); String str="我愛中國"; osw.write(str,0,str.length()-2); osw.flush(); osw.close(); }
FileWriter:類從OutputStreamWriter類繼承而來。該類按字符向流中寫入數據,可以關聯源文件。主要夠造函數:
FileWriter fw = new FileWriter(String fileName);//創建字符輸出流類對象和已存在的文件相關聯。文件不存在的話,並創建。 如:FileWriter fw = new FileWriter("C:\\demo.txt"); FileWriter fw = new FileWriter(String fileName,boolean append);//創建字符輸出流類對象和已存在的文件相關聯,並設置該該流對文件的操作是否為續寫。 // 如:FileWriter fw = new FileWriter("C:\\demo.txt",ture); //表示在fw對文件再次寫入時,會在該文件的結尾續寫,並不會覆蓋掉。
該類寫文件方式與OutputStreamWriter基本一樣,例如:
public static void test() throws IOException{ FileWriter fw=new FileWriter("E:\\test\\javaIo\\4.txt"); String str="愛我中華"; fw.write(str); fw.flush(); fw.close(); }
BufferedWriter(緩沖流):字節輸出緩沖流,將文本寫入字符輸出流,緩沖各個字符,從而提供單個字符、數組和字符串的高效寫入。 可以指定緩沖區的大小,或者接受默認的大小。在大多數情況下,默認值就足夠大了。 該類提供了 newLine() 方法,它使用平台自己的行分隔符概念,此概念由系統屬性 line.separator 定義。該類寫入文本的方法就是父類Writer提供的write()系列方法,在這里我們演示他特有的newLine( )方法。
public static void newLineTest() throws IOException{ BufferedWriter bw=new BufferedWriter(new FileWriter("E:\\test\\javaIo\\2.txt")); bw.write("愛我"); bw.newLine(); bw.write("中華"); bw.flush(); bw.close(); }
//結果:(加入了換行符)
愛我
中華
四、文本文件的copy
1、使用轉換流copy文件:
/** * 在這里使用了指定編碼方式的對象,一般我們使用平台默認編碼方式的夠造函數創建對象 * @throws IOException */ public static void fileCopy() throws IOException{ InputStreamReader isr=new InputStreamReader(new FileInputStream("E:\\test\\javaIo\\1.txt"),"utf-8"); OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("E:\\test\\javaIo\\4.txt"),"utf-8"); char [] ch=new char[1024]; int len=0; while((len=isr.read(ch))!=-1){ osw.write(ch,0,len); osw.flush(); } osw.close(); isr.close(); }
2、使用關聯源文件的字符流copy文件:
public static void fileCopyFile() throws IOException{ FileReader fr=new FileReader("E:\\test\\javaIo\\1.txt"); FileWriter fw=new FileWriter("E:\\test\\javaIo\\2.txt"); char [] ch=new char[1024]; int len=0; while((len=fr.read(ch))!=-1){ fw.write(ch, 0, len); fw.flush(); } fw.close(); fr.close(); }
3、字節緩沖流+轉換流實現文件的copy
public static void fileCopy() throws IOException{ BufferedReader br=new BufferedReader(new InputStreamReader(new FileInputStream("E:\\test\\javaIo\\1.txt"))); BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(new FileOutputStream("E:\\test\\javaIo\\2.txt"))); char [] ch=new char[1024]; int len=0; while((len=br.read(ch))!=-1){ bw.write(ch, 0, len); bw.flush(); } bw.close(); br.close(); }
4、字節緩沖流+源文件字符流實現文件copy
public static void bufferedReaderTest1() throws IOException{ BufferedReader br=new BufferedReader(new FileReader("E:\\test\\javaIo\\1.txt")); BufferedWriter bw=new BufferedWriter(new FileWriter("E:\\test\\javaIo\\2.txt")); char [] ch=new char[1024]; int len=0; while((len=br.read(ch))!=-1){ bw.write(ch, 0, len); bw.flush(); } bw.close(); br.close(); }
5、使用緩沖流特定的行方法(readLine(),newLine() )實現文件的copy
//BufferedReader public String readLine() throws IOException //讀取一個文本行。通過下列字符之一即可認為某行已終止:換行 ('\n')、回車 ('\r') 或回車后直接跟着換行。 // BufferedWriter bw.newLine();//寫入一個行分隔符。行分隔符字符串由系統屬性 line.separator 定義,並且不一定是單個新行 ('\n') 符。 public static void copyLine() throws IOException{ BufferedReader br=new BufferedReader(new FileReader("E:\\test\\javaIo\\1.txt")); BufferedWriter bw=new BufferedWriter(new FileWriter("E:\\test\\javaIo\\2.txt")); String str=null; while((str=br.readLine())!=null){ bw.write(str); bw.newLine();//寫入一個行分隔符。行分隔符字符串由系統屬性 line.separator 定義,並且不一定是單個新行 ('\n') 符。 bw.flush(); } bw.close(); br.close(); }