详细领略Java的输入流和输出流
狼北前言
說到java的輸入流和輸出流,IO流分為兩種的(字節流和字符流),那么我們首先就得了解什么是java的字節流和字符流,然后我才來了解怎么操作這些流來寫入文件中和寫出文件。所以我這篇文章分為兩大部分,第一部分是“IO流”,第二部分是“操作io流”。廢話不多說讓猿猴帶你一起來領略這里面的一二。(對博主感興趣可以加博主的抖音號langbei57048)
Java的IO流
什么是流
Java中的流是對字節序列的抽象,我們可以想象有一個水管,只不過現在流動在水管中的不再是水,而是字節序列。和水流一樣,Java中的流也具有一個“流動的方向”,通常可以從中讀入一個字節序列的對象被稱為輸入流;能夠向其寫入一個字節序列的對象被稱為輸出流。
字節流
Java中的字節流處理的最基本單位為單個字節(字節規定一個字節由八個二進制位構成)它通常用來處理二進制數據。Java中最基本的兩個字節流類是InputStream和OutputStream,它們分別代表了最基本的輸入字節流和輸出字節流。
字符流
Java中的字符流處理的最基本的單元是Unicode碼元(大小2字節),它通常用來處理文本數據。所謂Unicode碼元,也就是一個Unicode代碼單元,范圍是0x0000~0xFFFF。在以上范圍內的每個數字都與一個字符相對應,Java中的String類型默認就把字符以Unicode規則編碼而后存儲在內存中。然而與存儲在內存中不同,存儲在磁盤上的數據通常有著各種各樣的編碼方式。使用不同的編碼方式,相同的字符會有不同的二進制表示。實際上字符流是這樣工作的:
輸出字符流:把要寫入文件的字符序列(實際上是Unicode碼元序列)轉為指定編碼方式下的字節序列,然后再寫入到文件中;
輸入字符流:把要讀取的字節序列按指定編碼方式解碼為相應字符序列(實際上是Unicode碼元序列)從而可以存在內存中。
字符流與字節流的區別
經過以上的描述,我們可以知道字節流與字符流之間主要的區別體現在以下幾個方面:
字節流操作(讀寫)的基本單元為字節;字符流操作的基本單元為Unicode碼元。
字節流默認不使用緩沖區;字符流使用緩沖區。
字節流通常用于處理二進制數據,實際上它可以處理任意類型的數據,但它不支持直接寫入或讀取Unicode碼元;字符流通常處理文本數據,它支持寫入及讀取Unicode碼元。
只是讀寫文件,和文件內容無關的,一般選擇字節流。
字節流使用場景
字節流適合所有類型文件的數據傳輸,因為計算機字節(Byte)是電腦中表示信息含義的最小單位,因為在通常情況下一個ACSII碼就是一個字節的空間來存放。
字符流使用場景
字符流只能夠處理純文本數據(文本文件),其他類型數據不行,但是字符流處理文本要比字節流處理文本要方便。
字符流按字符讀數據:一次讀兩個字節,返回了這兩個字節所對應的字符的int型數值(編碼)。寫入文件時把這兩個字節的內容解碼成這個字符在Unicode碼下對應的二進制數據寫入。即把原始文件中的二進制數據以字符形式讀出,再將字符以二進制形式寫入,所以得到的文件以字符方式存儲。而圖片的數據是按字節存儲的,所以打開圖片時解碼出錯了!字節流按字節讀數據:而字節不需要編碼、解碼,只有字節與字符之間轉換時才需要編碼、解碼!所以可以正常讀取圖片數據。
操作IO流
我們在進行Android java 開發的時候,經常會遇到各種IO流操作。IO流操作一般分為兩類:字符流和字節流。以“Reader”結尾都是字符流,操作的都是字符型的數據;以“Stream”結尾的都是字節流,操作的都是byte數據。現將各種常見IO流總結如下:
一、字節流
1.InputStream 和 OutputStream
InputStream 和 OutputStream為各種輸入輸出字節流的基類,所有字節流都繼承這兩個基類。
2.FileInputStream 和 FileOutputStream
這兩個從字面意思很容易理解,是對文件的字節流操作,也會最常見的IO操作流。
都是有兩個構造函數:
輸入流 FileinputStream(File file或者String filePath)
輸出流FileoutputStream(File file或者String filePath)
方法:
①read() :從輸入流中一次讀取一個字節,讀完一個字節自動讀取數據的下一個字節,返回0到255范圍內的int字節值。如果因為已經到達流末尾而沒有可用的字節,則返回*-1*。在輸入數據可用、檢測到流末尾或者拋出異常前,此方法一直阻塞。
理解read()讀取的字節為什么返回int型變量
1、方法解釋中的-1相當于是數據字典告訴調用者文件已到底,可以結束讀取了,這里的-1是Int型 那么當文件未到底時,我們讀取的是字節,若返回byte類型,那么勢必造成同一方法返回類型不同的情況這是不允許的2、我們讀取的字節實際是由8位二進制組成,二進制文件不利于直觀查看,可以轉成常用的十進制進行展示,因此需要把讀取的字節從二進制轉成十進制整數,故返回int型3、 因此結合以上3點,保證返回類型一致以及直觀查看的情況,因此該方法雖然讀取的是字節但返回int型②read(byte[] b) :從輸入流中讀取一定數量的字節,并將其讀取存儲在緩沖區數組 b 中。以整數形式返回實際讀取的字節數。在輸入數據可用、檢測到文件末尾或者拋出異常前,此方法一直阻塞。如果因為流位于文件末尾而沒有可用的字節,則返回值 -1;每次讀取的字節數是緩沖區數組的最大儲存字節數(new byte[2] 數組最多儲存2個字節,那么read(byte)每一次只能讀到2字節,并且數組是滿的狀態),下一次調用讀取的字節存入這個數組會替代掉上一次數組的內容(返回的是讀了多少個字節而讀到的字節內容儲存到了b緩沖區中)
eg:我們文本文件儲存的是123456
//注意下面我是簡單寫法,拋出異常那些都沒寫File file=new File("D://text.txt");FileInputStream fileInputStream=new FileInputStream(file); //獲取文件輸入流byte[] b=new byte[2]; //new一個字節數數據來當緩沖區,數組大小為2個字節 int i=0;while (n!=-1) //當n不等于-1,則代表沒到末尾{ n=fileInputStream.read(b);//讀取到緩存區中返回每一次實際讀取到字節數組中的字節數 System.out.println(n); //在文件內容字節足夠時讀到的就是2個字節,但是結尾不夠2 個字節了,那么讀到的就是剩下的字節數System.out.println(Arrays.toString(b)); //讀取后的字節數組內容 i++;System.out.println("執行次數:"+i); }System.out.println(new String(b));}? 結果是:read(b)因為我們設置b數組大小是2個字節,所以每次讀出都是倆2字節,但最后不夠2個字節(只剩下一個字節),所以read(b)返回讀到的字節數是1,但是緩存區的數組是【最后一個字節,前一次留下來的】,因為只剩一個字節只能替代b數據的第一項
③read(byte[] bytes,int off ,int len) 參數的byte照樣是緩存區數組,off是讀取字節的開始位置,len是每次讀取的的字節數
那么這個和上面read(b)的區別就是他指定了每一次讀出多少個字節,而不再用每一次讀滿一個緩存數組的形式,并且這一個可以指定讀取字節開始的位置。返回的也是實際每次讀取到的字節數而不是字節,字節都儲存在緩存數組里。(比如,new bety[10], read(bety,0,9),那么每次讀進數組的是9個字節,那么每次數據就會剩下一個字節項,那么下一次用read讀,讀到的第一個字節就會放在數組的最后一個位置,而剩下的8個字節替換掉上次的前8個字節,想想就復雜 )。
我們常用的是read(b)接下來我們來完整的演示使用過程吧!
/* * 以字節為單位讀取文件,常用于讀二進制文件,如圖片、聲音、影像等文件。*/public static void readFileByBytes(String inFile, String outFile) {File file = new File(fileName); //①寫好文件路徑try {byte[] tempbytes = new byte[100]; //②創建數組當緩存區int byteread = 0;InputStream in = new FileInputStream(inFile); //③獲取文件輸入流OutputStream out = new FileOutputStream(outFile); //④獲取打開文件while ((byteread = in.read(tempbytes)) != -1) {//⑤每次讀取100個字節進入緩存數組out.write(tempbytes, 0, byteread); //⑥立即從緩沖區中寫入文件,每次寫入的字節數必須和實際讀到的字節數相同}} catch (Exception e1) {e1.printStackTrace();} finally {if (in != null) {try {in.close();} catch (IOException e1) {}try {out.close();} catch (IOException e1) {}}}}3.BufferedInputStream 和 BufferedOutputStream
BufferedInputStream是帶緩沖區的輸入流,它繼承于FilterInputStream。默認緩沖區大小是8M,能夠減少訪問磁盤的次數,提高文件讀取性能。
BufferedOutputStream是帶緩沖區的輸出流,它繼承于FilterOutputStream,能夠提高文件的寫入效率。
它們提供的“緩沖功能”本質上是通過一個內部緩沖區數組實現的。例如,在新建某輸入流對應的BufferedInputStream后,當我們通過read()讀取輸入流的數據時,BufferedInputStream會將該輸入流的數據分批的填入到緩沖區中。每當緩沖區中的數據被讀完之后,輸入流會再次填充數據緩沖區;如此反復,直到我們讀完輸入流數據。(其實書寫的步驟是一樣的,內在區別)
public static void readAndWrite(String[] args) { try {BufferedInputStream bis = new BufferedInputStream(new FileInputStream("f:/a.mp3"));BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("f:/b.mp3"));byte[] b=new byte[1024];int len=0;while(-1!= (len = bis.read(b, 0, b.length))) {bos.write(b, 0, len);}} catch (FileNotFoundException e) {System.out.println("文件找不到");e.printStackTrace();} catch (IOException e) {e.printStackTrace();}finally{if (null! = bos){bos.close();}if (null! = bis){bis.close();}} }4.DataInputStream和DataOutputStream
DataInputStream 是數據輸入流,它繼承于FilterInputStream。
DataOutputStream 是數據輸出流,它繼承于FilterOutputStream。
二者配合使用,“允許應用程序以與機器無關方式從底層輸入流中讀寫基本 Java 數據類型”。
二.字符流
二、字符流
1.InputStreamReader 和 OutputStreamWriter
InputStreamReader 和 OutputStreamWriter為各種輸入輸出字符流的基類,所有字符流都繼承這兩個基類。實際上,這兩個類內部各自持有一個inputStream 和 outputStream對象,相當于是對inputStream 和 outputStream進行了包裝,將輸入字節流轉換成字符流,便于讀寫操作。
/*** 以字符為單位讀取文件,常用于讀文本,數字等類型的文件*///用reader的read()一次只讀一個字符public static void readFileByChars(String fileName) {File file = new File(fileName);Reader reader = null;try {System.out.println("以字符為單位讀取文件內容,一次讀一個字節:");//1. 一次讀一個字符 reader = new InputStreamReader(new FileInputStream(file));//可以是任意的InputStream類,不一定必須FileInputStreamint tempchar;while ((tempchar = reader.read()) != -1) {if (((char) tempchar) != '\r') {System.out.print((char) tempchar);}}reader.close();} catch (Exception e) {e.printStackTrace();}----------------------------------------------------------------------------------------//用用reader的read(b)一次讀數組大小個字符try {System.out.println("以字符為單位讀取文件內容,一次讀多個字節:");//2. 一次讀多個字符char[] tempchars = new char[30];int charread = 0;reader = new InputStreamReader(new FileInputStream(fileName));while ((charread = reader.read(tempchars)) != -1) {for (int i = 0; i < charread; i++) {if (tempchars[i] != '\r') {System.out.print(tempchars[i]);}}}} catch (Exception e1) {e1.printStackTrace();} finally {if (reader != null) {try {reader.close();} catch (IOException e1) {}}} }2.FileReader 和 FileWriter
FileReader 和 FileWriter分別繼承自 inputStreamReader 和 outputStreamWriter。它是對讀取文件操作系統的封裝,所有的讀寫都是直接操作文件系統。因此如果是頻繁讀寫操作,不建議使用FileReader 和 FileWriter,性能將會非常低,這時你需要使用BufferedReader。
(1)FileWriter類
構造方法:
FileWriter fw = new FileWriter(String fileName); //創建字符輸出流類對象和已存在的文件相關聯。文件不存在的話,并創建。
FileWriter fw = new FileWriter(String fileName,boolean append); //創建字符輸出流類對象和已存在的文件相關聯,并設置該該流對文件的操作是否為續寫。
主要方法:
write(char[] buffer, int offset, int count) //將字符數組寫入,offset為數組的起始地址,count為需要寫入的字符數
void write(String str) //寫入字符串。當執行完此方法后,字符數據還并沒有寫入到目的文件中去。此時字符數據會保存在緩沖區中。
viod flush() //刷新該流中的緩沖。將緩沖區中的字符數據保存到目的文件中去。
viod close() //關閉此流。在關閉前會先刷新此流的緩沖區。在關閉后,再寫入或者刷新的話,會拋IOException異常。
(2)FileReader類
構造方法:
FileReader fr = new FileReader(String fileName); //使用帶有指定文件的String參數的構造方法。創建該輸入流對象。并關聯源文件。
主要方法:
int read(); // 讀取單個字符。返回作為整數讀取的字符,如果已達到流末尾,則返回 -1。
int read(char []cbuf); //將字符讀入數組。返回讀取的字符數。如果已經到達尾部,則返回-1。
void close(); //關閉此流對象。釋放與之關聯的所有資源。
4.BufferedReader 和 BufferedWriter
(1)BufferedReader和BufferedWriter類各擁有8192字符的緩沖區。當BufferedReader在讀取文本文件時,會先盡量從文件中讀入字符數據并置入緩沖區,而之后若使用read()方法,會先從緩沖區中進行讀取。如果緩沖區數據不足,才會再從文件中讀取,使用BufferedWriter時,寫入的數據并不會先輸出到目的地,而是先存儲至緩沖區中。如果緩沖區中的數據滿了,才會一次對目的地進行寫出。
(2)從標準輸入流System.in中直接讀取使用者輸入時,使用者每輸入一個字符,System.in就讀取一個字符。為了能一次讀取一行使用者的輸入,使用了BufferedReader來對使用者輸入的字符進行緩沖。readLine()方法會在讀取到使用者的換行字符時,再一次將整行字符串傳入
System.in是一個位流,為了轉換為字符流,可使用InputStreamReader為其進行字符轉換,然后再使用BufferedReader為其增加緩沖功能。例如:
public static void readAndWrite() { try { //緩沖System.in輸入流 //System.in是位流,可以通過InputStreamReader將其轉換為字符流 BufferedReader bufReader = new BufferedReader(new InputStreamReader(System.in)); //緩沖FileWriter BufferedWriter bufWriter = new BufferedWriter(new FileWriter("/sdcard/log/test.txt")); String input = null; //每讀一行進行一次寫入動作 while(!(input = bufReader.readLine())) { bufWriter.write(input); //newLine()方法寫入與操作系統相依的換行字符,依執行環境當時的OS來決定該輸出那種換行字符 bufWriter.newLine(); } bufReader.close(); bufWriter.close(); } catch(ArrayIndexOutOfBoundsException e) { System.out.println("沒有指定文件"); } catch(IOException e) { e.printStackTrace(); }}三、 RandomAccessFile
RandomAccessFile不屬于InputStream和OutputStream類系的。實際上,它和這兩個類系毫不相干,甚至不使用InputStream和OutputStream類中已經存在的任何功能;它是一個完全獨立的類。這可能是因為RandomAccessFile能在文件里面前后移動,所以它的行為與其它的I/O類有些根本性的不同。
RandomAccessFile的基本功能有:定位用的getFilePointer( ),在文件里移動用的seek( ),以及判斷文件大小的length( )、skipBytes()跳過多少字節數。此外,它的構造函數還要一個表示以只讀方式(“r”),還是以讀寫方式(“rw”)打開文件的參數。實際它和C的fopen()一模一樣,都是直接對文件句柄操作。
總結
以上是生活随笔為你收集整理的详细领略Java的输入流和输出流的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 表哥的Access入门++以Excel视
- 下一篇: 超宽带 DWM1000模块 简介