您好,登錄后才能下訂單哦!
字節(jié)流和字符流
對于文件必然有讀和寫的操作,讀和寫就對應(yīng)了輸入和輸出流,流又分成字節(jié)和字符流。
1.從對文件的操作來講,有讀和寫的操作——也就是輸入和輸出。
2.從流的流向來講,有輸入和輸出之分。
3.從流的內(nèi)容來講,有字節(jié)和字符之分。
這篇文章先后講解IO流中的字節(jié)流和字符流的輸入和輸出操作。
一、字節(jié)流
1)輸入和輸出流
首先,字節(jié)流要進行讀和寫,也就是輸入和輸出,所以它有兩個抽象的父類InputStream、OutputStream。
InputStream抽象了應(yīng)用程序讀取數(shù)據(jù)的方式,即輸入流。
OutputStream抽象了應(yīng)用程序?qū)懗鰯?shù)據(jù)的方式,即輸出流。
2)讀寫結(jié)束
在字節(jié)流中當(dāng)讀寫結(jié)束,達到文件結(jié)尾時,稱為EOF = End或者讀到-1就讀到結(jié)尾。
3)輸入流基本方法
首先我們要清楚輸入流是什么。比如通過我們的鍵盤在文本文件上輸入內(nèi)容,這個過程鍵盤充當(dāng)?shù)木褪禽斎肓?,而不是輸出流。因為鍵盤的功能是將內(nèi)容輸入到系統(tǒng),系統(tǒng)再寫入到文件上。以下是輸入流的基本方法read():
int b = in.read(); //讀取一個字節(jié)無符號填充到int低八位。-1是EOF。 in.read(byte[] buf); //讀取數(shù)據(jù)填充到字節(jié)數(shù)組buf中。返回的是讀到的字節(jié)個數(shù)。 in.read(byte[] buf,int start, int size)//讀取數(shù)據(jù)到字節(jié)數(shù)組buf從buf的start位置開始存放size長度分數(shù)據(jù)
其中in是InputStream抽象類的實例,可以發(fā)現(xiàn)這個方法和RandomAccessFile類中的read()方法差不多,因為兩者都是通過字節(jié)來讀取的。
4)輸出流基本方法
輸出流是進行寫的操作,其基本操作方法是write(),可以將此方法與輸入read()方法一 一去對應(yīng),更好理解。
out.write(int b)//寫出一個byte到流,b的低8位 out.write(byte[] buf)//將buf字節(jié)數(shù)組都寫到流 out.write(byte[] buf, int start,int size) //字節(jié)數(shù)組buf從start位置開始寫size長度的字節(jié)到流
了解了InputStream、OutputStream的基本操作方法后,再來看看它們兩個的“孩子”FileInputStream和FileOutputStream。
這兩個子類具體實現(xiàn)了在文件上讀取和寫入數(shù)據(jù)的操作,日程編程中更多的是使用這兩個類。
二、FileInputStream和FileOutputStream類的使用
-----------------FileInputStream類的使用
1.使用read()方法讀取文件
/** * 讀取指定文件內(nèi)容,按照16進制輸出到控制臺 * 并且每輸出10個byte換行 * @throws FileNotFoundException */ public static void printHex(String fileName) throws IOException{ //把文件作為字節(jié)流進行讀操作 FileInputStream in=new FileInputStream(fileName); int b; int count=0;//計數(shù)讀到的個數(shù) while((b=in.read())!=-1){ if(b<=0xf){ //單位數(shù)前面補0 System.out.println("0"); } System.out.print(Integer.toHexString(b& 0xff)+" "); if(++count%10==0){ System.out.println(); } } in.close();//一定要關(guān)閉流 }
運行結(jié)果(隨便一個文件來測試的):
注意:
FileInputStream()構(gòu)造函數(shù)可以通過文件名(String)也可以通過File對象。上面的案例是使用文件名來構(gòu)造的。
(b=in.read())!=-1 通過讀到-1來判斷是否讀到文件結(jié)尾。
in.close() 使用完IO流的對象一定要關(guān)閉流,養(yǎng)成好習(xí)慣很重要。
2.使用read(byte[] buf,int start, int size)方法讀取文件
上述方法只能一個一個字節(jié)讀取,對于較大的文件效率太低,推薦使用這個方法來一次性讀取文件。
public static void printHexByBytes(String fileName) throws IOException{ FileInputStream in=new FileInputStream(fileName); byte[] buf=new byte[20*1024];//開辟一個20k大小的字節(jié)數(shù)組 /* * 從in中批量讀取字節(jié),放入到buf這個字節(jié)數(shù)組中 * 從第0個位置開始放,最多放buf.length個 * 返回的是讀到的字節(jié)個數(shù) */ //一次性讀完的情況 int count=in.read(buf, 0, buf.length); int j=1; for(int i=0;i<count;i++){ if((buf[i]&0xff)<=0xf){ //單位數(shù)前面補0 System.out.print("0"); } System.out.print(Integer.toHexString(buf[i]&0xff)+ " "); if(j++%10==0){ System.out.println(); } } in.close(); } }
read(byte[] buf,int start, int size)返回的是讀到的字節(jié)個數(shù),即buf字節(jié)數(shù)組的有效長度,所以輸出buf數(shù)組時用的長度是count而不是buf.length,因為我們不知道文件大小和數(shù)組大小的關(guān)系,上述方法適用于文件大小不超過數(shù)組大小的情況下,一次性把文件內(nèi)容讀取到數(shù)組里,這里就有一個問題了,如果文件大小超過數(shù)組大小,那又該如何讀取才能把文件全部讀完呢??
我們知道讀到-1就是讀到文件末,所以還是利用while循環(huán)重復(fù)讀取直到讀到-1結(jié)束循環(huán),把上述代碼修改后如下:
public static void printHexByBytes(String fileName) throws IOException{ FileInputStream in=new FileInputStream(fileName); byte[] buf=new byte[20*1024];//開辟一個20k大小的字節(jié)數(shù)組 /* * 從in中批量讀取字節(jié),放入到buf這個字節(jié)數(shù)組中 * 從第0個位置開始放,最多放buf.length個 * 返回的是讀到的字節(jié)個數(shù) */ int j=1; //一個字節(jié)數(shù)組讀不完的情況,用while循環(huán)重復(fù)利用此數(shù)組直到讀到文件末=-1 int b=0; while((b=in.read(buf, 0, buf.length))!=-1){ for(int i=0;i<b;i++){ if((buf[i]&0xff)<=0xf){ //單位數(shù)前面補0 System.out.print("0"); } System.out.print(Integer.toHexString(buf[i]&0xff)+ " "); if(j++%10==0){ System.out.println(); } } } in.close(); } }
好了,我們用一個大于數(shù)組的文件來測試一下結(jié)果(太長,只截圖末尾):
大家可以比較兩者的不同,第二種優(yōu)化后更適合日常的使用,因為無論文件大小我們都可以一次性直接讀完。
-----------------FileOutputStream類的使用
FileOutputStream類和FileInputStream類的使用相類似,它實現(xiàn)了向文件中寫出btye數(shù)據(jù)的方法。里面的一些細節(jié)跟FileInputStream差不多的我就不提了,大家自己可以理解的。
1.構(gòu)造方法
FileOutputStream類構(gòu)造時根據(jù)不同的情況可以使用不同的方法構(gòu)造,如:
//如果該文件不存在,則直接創(chuàng)建,如果存在,刪除后創(chuàng)建 FileOutputStream out = new FileOutputStream("demo/new1.txt");//以路徑名稱構(gòu)造
//如果該文件不存在,則直接創(chuàng)建,如果存在,在文件后追加內(nèi)容 FileOutputStream out = new FileOutputStream("demo/new1.txt",true); 更多內(nèi)容可以查詢API。
2.使用write()方法寫入文件
write()方法和read()相似,只能操作一個字節(jié),即只能寫入一個字節(jié)。例如:
out.wirte(‘A');//寫出了‘A'的低八位 int a=10;//wirte只能寫八位,那么寫一個int需要寫4次,每次八位 out.write(a>>>24); out.write(a>>>16); out.write(a>>>8); out.wirte(a);
每次只寫一個字節(jié),顯然是不效率的,OutputStream當(dāng)然跟InputStream一樣可以直接對byte數(shù)組操作。
3.使用write(byte[] buf,int start, int size)方法寫入文件
意義:把byte[]數(shù)組從start位置到size位置結(jié)束長度的字節(jié)寫入到文件中。
語法格式和read相同,不多說明
三、FileInputStream和FileOutputStream結(jié)合案例
了解了InputStream和OutputStream的使用方法,這次結(jié)合兩者來寫一個復(fù)制文件的方法。
public static void copyFile(File srcFile,File destFile)throws IOException{ if(!srcFile.exists()){ throw new IllegalArgumentException("文件:"+srcFile+"不存在"); } if(!srcFile.isFile()){ throw new IllegalArgumentException(srcFile+"不是一個文件"); } FileInputStream in =new FileInputStream(srcFile); FileOutputStream out =new FileOutputStream(destFile); byte[] buf=new byte[8*1024]; int b; while((b=in.read(buf, 0, buf.length))!=-1){ out.write(buf, 0, b); out.flush();//最好加上 } in.close(); out.close(); }
測試文件案例:
try { IOUtil.copyFile(new File("C:\\Users\\acer\\workspace\\encode\\new4\\test1"), new File("C:\\Users\\acer\\workspace\\encode\\new4\\test2")); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
運行結(jié)果:
復(fù)制成功!
四、DataInputStream和DataOutputStream的使用
DataInputStream、DataOutputStream 是對“流”功能的擴展,可以更加方便地讀取int,long。字符等類型的數(shù)據(jù)。
對于DataOutputStream而言,它多了一些方法,如
writeInt()/wirteDouble()/writeUTF()
這些方法其本質(zhì)都是通過write()方法來完成的,這些方法都是經(jīng)過包裝,方便我們的使用而來的。
1.構(gòu)造方法
以DataOutputStream為例,構(gòu)造方法內(nèi)的對象是OutputStream類型的對象,我們可以通過構(gòu)造FileOutputStream對象來使用。
String file="demo/data.txt"; DataOutputStream dos= new DataOutputStream(new FileOutputStream(file));
2.write方法使用
dos.writeInt(10); dos.writeInt(-10); dos.writeLong(10l); dos.writeDouble(10.0); //采用utf-8編碼寫出 dos.writeUTF("中國"); //采用utf-16be(java編碼格式)寫出 dos.writeChars("中國");
3.read方法使用
以上述的寫方法對立,看下面例子用來讀出剛剛寫的文件
String file="demo/data.txt"; IOUtil.printHex(file); DataInputStream dis=new DataInputStream(new FileInputStream(file)); int i=dis.readInt(); System.out.println(i); i=dis.readInt(); System.out.println(i); long l=dis.readLong(); System.out.println(l); double d=dis.readDouble(); System.out.println(d); String s= dis.readUTF(); System.out.println(s); dis.close();
運行結(jié)果:
總結(jié):DataInputStream和DataOutputStream其實是對FileInputStream和FileOutputStream進行了包裝,通過嵌套方便我們使用FileInputStream和FileOutputStream的讀寫操作,它們還有很多其他方法,大家可以查詢API。
注意:進行讀操作的時候如果類型不匹配會出錯!
五、字節(jié)流的緩沖流BufferredInputStresam&BufferredOutputStresam
這兩個流類為IO提供了帶緩沖區(qū)的操作,一般打開文件進行寫入或讀取操作時,都會加上緩沖,這種流模式提高了IO的性能。
從應(yīng)用程序中把輸入放入文件,相當(dāng)于將一缸水倒入另一個缸中:
FileOutputStream---->write()方法相當(dāng)于一滴一滴地把水“轉(zhuǎn)移”過去
DataOutputStream---->write()XXX方法會方便一些,相當(dāng)于一瓢一瓢地把水“轉(zhuǎn)移”過去
BufferedOutputStream---->write方法更方便,相當(dāng)于一瓢一瓢水先放入一個桶中(緩沖區(qū)),再從桶中倒入到一個缸中。提高了性能,推薦使用!
上述提到過用FileInputStream和FileOutputStream結(jié)合寫的一個拷貝文件的案例,這次通過字節(jié)的緩沖流對上述案例進行修改,觀察兩者的區(qū)別和優(yōu)劣。
主函數(shù)測試:
try { long start=System.currentTimeMillis(); //IOUtil.copyFile(new File("C:\\Users\\acer\\Desktop\\學(xué)習(xí)路徑.docx"), new File("C:\\Users\\acer\\Desktop\\復(fù)制文本.docx")); long end=System.currentTimeMillis(); System.out.println(end-start); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }
(1)單字節(jié)進行文件的拷貝,利用帶緩沖的字節(jié)流
/* * 單字節(jié)進行文件的拷貝,利用帶緩沖的字節(jié)流 */ public static void copyFileByBuffer(File srcFile,File destFile)throws IOException{ if(!srcFile.exists()){ throw new IllegalArgumentException("文件:"+srcFile+"不存在"); } if(!srcFile.isFile()){ throw new IllegalArgumentException(srcFile+"不是一個文件"); } BufferedInputStream bis=new BufferedInputStream(new FileInputStream(srcFile)); BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream(destFile)); int c; while((c=bis.read())!=-1){ bos.write(c); bos.flush();//刷新緩沖區(qū) } bis.close(); bos.close(); }
運行結(jié)果(效率):
(2)單字節(jié)不帶緩沖進行文件拷貝
/* * 單字節(jié)不帶緩沖進行文件拷貝 */ public static void copyFileByByte(File srcFile,File destFile)throws IOException{ if(!srcFile.exists()){ throw new IllegalArgumentException("文件:"+srcFile+"不存在"); } if(!srcFile.isFile()){ throw new IllegalArgumentException(srcFile+"不是一個文件"); } FileInputStream in=new FileInputStream(srcFile); FileOutputStream out=new FileOutputStream(destFile); int c; while((c=in.read())!=-1){ out.write(c); out.flush();//不帶緩沖,可加可不加 } in.close(); out.close(); }
運行結(jié)果(效率):
(3)批量字節(jié)進行文件的拷貝,不帶緩沖的字節(jié)流(就是上面第三點最初的案例的代碼)
/* * 字節(jié)批量拷貝文件,不帶緩沖 */ public static void copyFile(File srcFile,File destFile)throws IOException{ if(!srcFile.exists()){ throw new IllegalArgumentException("文件:"+srcFile+"不存在"); } if(!srcFile.isFile()){ throw new IllegalArgumentException(srcFile+"不是一個文件"); } FileInputStream in =new FileInputStream(srcFile); FileOutputStream out =new FileOutputStream(destFile); byte[] buf=new byte[8*1024]; int b; while((b=in.read(buf, 0, buf.length))!=-1){ out.write(buf, 0, b); out.flush();//最好加上 } in.close(); out.close(); }
運行結(jié)果(效率):
(4)批量字節(jié)進行文件的拷貝,帶緩沖的字節(jié)流(效率最高,推薦使用?。。?/p>
/* * 多字節(jié)進行文件的拷貝,利用帶緩沖的字節(jié)流 */ public static void copyFileByBuffers(File srcFile,File destFile)throws IOException{ if(!srcFile.exists()){ throw new IllegalArgumentException("文件:"+srcFile+"不存在"); } if(!srcFile.isFile()){ throw new IllegalArgumentException(srcFile+"不是一個文件"); } BufferedInputStream bis=new BufferedInputStream(new FileInputStream(srcFile)); BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream(destFile)); byte[] buf=new byte[20*1024]; int c; while((c=bis.read(buf, 0, buf.length))!=-1){ bos.write(buf, 0, c); bos.flush();//刷新緩沖區(qū) } bis.close(); bos.close(); }
運行結(jié)果(效率):
注意:
批量讀取或?qū)懭胱止?jié),帶字節(jié)緩沖流的效率最高,推薦使用此方法。
當(dāng)使用字節(jié)緩沖流時,寫入操作完畢后必須刷新緩沖區(qū),flush()。
不使用字節(jié)緩沖流時,flush()可以不加,但是最好加上去。
六、字符流
首先我們需要了解以下概念。
1)需要了解編碼問題---->轉(zhuǎn)移至《計算機中的編碼問題》
2)認識文本和文本文件
java的文本(char)是16位無符號整數(shù),是字符的unicode編碼(雙字節(jié)編碼)
文件是byte byte byte...的數(shù)據(jù)序列
文本文件是文本(char)序列按照某種編碼方案(utf-8,utf-16be,gbk)序列化byte的存儲
3)字符流(Reader Writer)
字符的處理,一次處理一個字符;
字符的底層依然是基本的字節(jié)序列;
4)字符流的基本實現(xiàn)
InputStreamReader:完成byte流解析成char流,按照編碼解析。
OutputStreamWriter:提供char流到byte流,按照編碼處理。
-------------------------Reader和Writer的基本使用-------------------------------
String file1="C:\\Users\\acer\\workspace\\encode\\new4\\test1"; String file2="C:\\Users\\acer\\workspace\\encode\\new4\\test2"; InputStreamReader isr=new InputStreamReader(new FileInputStream(file1)); OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream(file2)); // int c; // while((c=isr.read())!=-1){ // System.out.print((char)c); // } char[] buffer=new char[8*1024]; int c; //批量讀取,放入buffer這個字符數(shù)組,從第0個位置到數(shù)組長度 //返回的是讀到的字符個數(shù) while((c=isr.read(buffer,0,buffer.length))!=-1){ String s=new String(buffer,0,c);//將char類型數(shù)組轉(zhuǎn)化為String字符串 System.out.println(s); osw.write(buffer,0,c); osw.flush(); //osw.write(s); //osw.flush(); } isr.close(); osw.close();
注意:
字符流操作的是文本文件,不能操作其他類型的文件??!
默認按照GBK編碼來解析(項目默認編碼),操作文本文件的時候,要寫文件本身的編碼格式(在構(gòu)造函數(shù)時在后面加上編碼格式)!!
字符流和字節(jié)流的區(qū)別主要是操作的對象不同,還有字符流是以字符為單位來讀取和寫入文件的,而字節(jié)流是以字節(jié)或者字節(jié)數(shù)組來進行操作的??!
在使用字符流的時候要額外注意文件的編碼格式,一不小心就會造成亂碼!
七、字符流的文件讀寫流FileWriter和FileReader
跟字節(jié)流的FileInputStream和FileOutputStream類相類似,字符流也有相應(yīng)的文件讀寫流FileWriter和FileReader類,這兩個類主要是對文本文件進行讀寫操作。
FileReader/FileWriter:可以直接寫文件名的路徑。
與InputStreamReader相比壞處:無法指定讀取和寫出的編碼,容易出現(xiàn)亂碼。
FileReader fr = new FileReader("C:\\Users\\acer\\workspace\\encode\\new4\\test1"); //輸入流 FileWriter fw = new FileWriter(C:\\Users\\acer\\workspace\\encode\\new4\\test2");//輸出流
char[] buffer=new char[8*1024]; int c; while((c=fr.read(buffer, 0, buffer.length))!=-1){ fw.write(buffer, 0, c); fw.flush(); } fr.close(); fw.close();
注意:FileReader和FileWriter不能增加編碼參數(shù),所以當(dāng)項目和讀取文件編碼不同時,就會產(chǎn)生亂碼。 這種情況下,只能回歸InputStreamReader和OutputStreamWriter。
八、字符流的過濾器BufferedReader&BufferedWriter
字符流的過濾器有BufferedReader和BufferedWriter/PrintWriter
除了基本的讀寫功能外,它們還有一些特殊的功能。
BufferedReader----->readLine 一次讀一行,并不識別換行
BufferedWriter----->write 一次寫一行,需要換行
PrintWriter經(jīng)常和BufferedReader一起使用,換行寫入比BufferedWriter更方便
定義方式:
BufferedReader br =new BufferedReader(new InputStreamReader(new FileInputStream(目錄的地址))) BufferedWriter br =new BufferedWriter(new InputStreamWriter(new FileOutputStream(目錄的地址))) PrintWriter pw=new PrintWriter(目錄/Writer/OutputStream/File);
使用方法:
//對文件進行讀寫操作 String file1="C:\\Users\\acer\\workspace\\encode\\new4\\test1"; String file2="C:\\Users\\acer\\workspace\\encode\\new4\\test2"; BufferedReader br = new BufferedReader(new InputStreamReader( new FileInputStream(file1))); BufferedWriter bw=new BufferedWriter(new OutputStreamWriter( new FileOutputStream(file2))); String line; while((line=br.readLine())!=null){ System.out.println(line);//一次讀一行,并不能識別換行 bw.write(line); //單獨寫出換行操作 bw.newLine(); bw.flush(); } br.close(); bw.close(); }
在這里我們可以使用PrintWriter來代替BufferedWriter做寫操作,PrintWriter相比BufferedWriter有很多優(yōu)勢:
構(gòu)造函數(shù)方便簡潔,使用靈活
構(gòu)造時可以選擇是否自動flush
利用println()方法可以實現(xiàn)自動換行,搭配BufferedReader使用更方便
使用方法:
String file1="C:\\Users\\acer\\workspace\\encode\\new4\\test1"; String file2="C:\\Users\\acer\\workspace\\encode\\new4\\test2"; BufferedReader br = new BufferedReader(new InputStreamReader( new FileInputStream(file1))); PrintWriter pw=new PrintWriter(file2); //PrintWriter pw=new PrintWriter(outputStream, autoFlush);//可以指定是否自動flush String line; while((line=br.readLine())!=null){ System.out.println(line);//一次讀一行,并不能識別換行 pw.println(line);//自動換行 pw.flush();//指定自動flush后不需要寫 } br.close(); pw.close(); }
注意:
可以使用BufferedReader的readLine()方法一次讀入一行,為字符串形式,用null判斷是否讀到結(jié)尾。
使用BufferedWriter的write()方法寫入文件,每次寫入后需要調(diào)用flush()方法清空緩沖區(qū);PrintWriter在構(gòu)造時可以指定自動flush,不需要再調(diào)用flush方法。
在寫入時需要注意寫入的數(shù)據(jù)中會丟失換行,可以在每次寫入后調(diào)用BufferedReader的newLine()方法或改用PrintWriter的println()方法補充換行。
通常將PrintWriter配合BufferedWriter使用。(PrintWriter的構(gòu)造方法,及使用方式更為簡單)。
以上這篇【Java IO流】字節(jié)流和字符流的實例講解就是小編分享給大家的全部內(nèi)容了,希望能給大家一個參考,也希望大家多多支持億速云。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。