Java使用FileInputStream流讀取文件示例詳解
一、File流概念
JAVA中針對(duì)文件的讀寫操作設(shè)置了一系列的流,其中主要有FileInputStream,FileOutputStream,FileReader,FileWriter四種最為常用的流
二、FileInputStream
1)FileInputStream概念
FileInputStream流被稱為文件字節(jié)輸入流,意思指對(duì)文件數(shù)據(jù)以字節(jié)的形式進(jìn)行讀取操作如讀取圖片視頻等
2)構(gòu)造方法
2.1)通過(guò)打開與File類對(duì)象代表的實(shí)際文件的鏈接來(lái)創(chuàng)建FileInputStream流對(duì)象
public FileInputStream(File file) throws FileNotFoundException{}
若File類對(duì)象的所代表的文件不存在;不是文件是目錄;或者其他原因不能打開的話,則會(huì)拋出FileNotFoundException
/** * * 運(yùn)行會(huì)產(chǎn)生異常并被撲捉--因?yàn)椴淮嬖趚xxxxxxx這樣的文件 */ public static void main(String[] args) { File file=new File("xxxxxxxx"); //根據(jù)路徑創(chuàng)建File類對(duì)象--這里路徑即使錯(cuò)誤也不會(huì)報(bào)錯(cuò),因?yàn)橹皇钱a(chǎn)生File對(duì)象,還并未與計(jì)算機(jī)文件讀寫有關(guān)聯(lián) try { FileInputStream fileInputStream=new FileInputStream(file);//與根據(jù)File類對(duì)象的所代表的實(shí)際文件建立鏈接創(chuàng)建fileInputStream對(duì)象 } catch (FileNotFoundException e) { System.out.println("文件不存在或者文件不可讀或者文件是目錄"); } }
2.2)通過(guò)指定的字符串參數(shù)來(lái)創(chuàng)建File類對(duì)象,而后再與File對(duì)象所代表的實(shí)際路徑建立鏈接創(chuàng)建FileInputStream流對(duì)象
public FileInputStream(String name) throws FileNotFoundException
通過(guò)查看源碼,發(fā)現(xiàn)該構(gòu)造方法等于是在第一個(gè)構(gòu)造方法的基礎(chǔ)上進(jìn)行延伸的,因此規(guī)則也和第一個(gè)構(gòu)造方法一致
public FileInputStream(String name) throws FileNotFoundException { this(name != null ? new File(name) : null); }
2.3)該構(gòu)造方法沒(méi)有理解---查看api是指使用的fdObj文件描述符來(lái)作為參數(shù),文件描述符是指與計(jì)算機(jī)系統(tǒng)中的文件的連接,前面兩個(gè)方法的源碼中最后都是利用文件描述符來(lái)建立連接的
public FileInputStream(FileDescriptor fdObj)
3)FileInputStream常用API
3.1)從輸入流中讀取一個(gè)字節(jié)返回int型變量,若到達(dá)文件末尾,則返回-1
public int read() throws IOException
理解讀取的字節(jié)為什么返回int型變量
1、方法解釋中的-1相當(dāng)于是數(shù)據(jù)字典告訴調(diào)用者文件已到底,可以結(jié)束讀取了,這里的-1是Int型
2、那么當(dāng)文件未到底時(shí),我們讀取的是字節(jié),若返回byte類型,那么勢(shì)必造成同一方法返回類型不同的情況這是不允許的
3、我們讀取的字節(jié)實(shí)際是由8位二進(jìn)制組成,二進(jìn)制文件不利于直觀查看,可以轉(zhuǎn)成常用的十進(jìn)制進(jìn)行展示,因此需要把讀取的字節(jié)從二進(jìn)制轉(zhuǎn)成十進(jìn)制整數(shù),故返回int型
4、 因此結(jié)合以上3點(diǎn),保證返回類型一致以及直觀查看的情況,因此該方法雖然讀取的是字節(jié)但返回int型
read方法讀取實(shí)例--最后輸出內(nèi)容和字符內(nèi)容一致是123
package com.test; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; public class FileStream { /** * * */ public static void main(String[] args) { //建立文件對(duì)象 File file=new File("C:\\Users\\Administrator\\Desktop\\1.txt"); try { //建立鏈接 FileInputStream fileInputStream=new FileInputStream(file); int n=0; StringBuffer sBuffer=new StringBuffer(); while (n!=-1) //當(dāng)n不等于-1,則代表未到末尾 { n=fileInputStream.read();//讀取文件的一個(gè)字節(jié)(8個(gè)二進(jìn)制位),并將其由二進(jìn)制轉(zhuǎn)成十進(jìn)制的整數(shù)返回 char by=(char) n; //轉(zhuǎn)成字符 sBuffer.append(by); } System.out.println(sBuffer.toString()); } catch (FileNotFoundException e) { System.out.println("文件不存在或者文件不可讀或者文件是目錄"); } catch (IOException e) { System.out.println("讀取過(guò)程存在異常"); } } }
3.2)從輸入流中讀取b.length個(gè)字節(jié)到字節(jié)數(shù)組中,返回讀入緩沖區(qū)的總字節(jié)數(shù),若到達(dá)文件末尾,則返回-1
public int read(byte[] b) throws IOException
1. 我們先設(shè)定一個(gè)緩沖區(qū)即字節(jié)數(shù)組用于存儲(chǔ)從流中讀取的字節(jié)數(shù)據(jù),該數(shù)組的長(zhǎng)度為N
2. 那么就是從流中讀取N個(gè)字節(jié)到字節(jié)數(shù)組中。但是注意返回的是讀入的總字節(jié)數(shù)而并不是N,說(shuō)明有的時(shí)候?qū)嶋H讀入的總字節(jié)數(shù)不一定等于數(shù)組的長(zhǎng)度
3. 文件的內(nèi)容是12345.那么流中一共有5個(gè)字節(jié),但是我們?cè)O(shè)定的字節(jié)數(shù)組長(zhǎng)度為2.那么會(huì)讀取幾次?每次情況是怎么樣的?
public class FileStream { public static void main(String[] args) { //建立文件對(duì)象 File file=new File("C:\\Users\\Administrator\\Desktop\\1.txt"); try { //建立鏈接 FileInputStream fileInputStream=new FileInputStream(file); int n=0; byte[] b=new byte[2]; int i=0; while (n!=-1) //當(dāng)n不等于-1,則代表未到末尾 { n=fileInputStream.read(b);//返回實(shí)際讀取到字節(jié)數(shù)組中的字節(jié)數(shù) System.out.println(n); System.out.println(Arrays.toString(b)); //讀取后的字節(jié)數(shù)組內(nèi)容 i++; System.out.println("執(zhí)行次數(shù):"+i); } System.out.println(new String(b)); } catch (FileNotFoundException e) { System.out.println("文件不存在或者文件不可讀或者文件是目錄"); } catch (IOException e) { System.out.println("讀取過(guò)程存在異常"); } } }
實(shí)際執(zhí)行結(jié)果如下:
可以看出,數(shù)組長(zhǎng)度為2,因此第一次讀取2個(gè)字節(jié)到數(shù)組中,數(shù)組已經(jīng)被填滿。流中還剩余3個(gè)字節(jié)繼續(xù)讀取
第二次讀取,仍然讀取2個(gè)字節(jié)到數(shù)組中,數(shù)組內(nèi)容被替換。此時(shí)流中只剩余1個(gè)字節(jié),根據(jù)API說(shuō)明,讀取數(shù)組長(zhǎng)度(2)個(gè)字節(jié)到數(shù)組中,但接下來(lái)已經(jīng)無(wú)法繼續(xù)讀取2個(gè)字節(jié)了, 是否就應(yīng)該停止了?
實(shí)際過(guò)程中并未停止,而是進(jìn)行了第三次讀取,只讀取了剩余1個(gè)字節(jié),并頂替到了數(shù)組的0下標(biāo)位置中。
接下來(lái)第4次讀取,才發(fā)現(xiàn)移到末尾,而后返回-1.停止讀取
所以此處存疑-----為什么當(dāng)剩余只有1個(gè)字節(jié),而要求是讀取2個(gè)字節(jié)時(shí),還可以繼續(xù)讀?。?/p>
那么我們查看此方法源碼,發(fā)現(xiàn)其本質(zhì)是調(diào)用的其它方法readBytes(b, 0, b.length);
public int read(byte b[]) throws IOException { return readBytes(b, 0, b.length); }
繼續(xù)查看readBytes(b, 0, b.length)方法是native方法代表該方法是有實(shí)現(xiàn)體的但不是在JAVA語(yǔ)言中實(shí)現(xiàn)的導(dǎo)致沒(méi)辦法看具體實(shí)現(xiàn)
但是可以理解參數(shù)b是我們?cè)O(shè)置的數(shù)組,0是int型,最后一個(gè)參數(shù)是數(shù)組的長(zhǎng)度
private native int readBytes(byte b[], int off, int len) throws IOException;
那么我們查看FileInputStream的父類InputStream,發(fā)現(xiàn)有關(guān)于這個(gè)方法的實(shí)現(xiàn),
我們現(xiàn)在考慮第三次讀取的時(shí)候方法執(zhí)行情況,此時(shí)b是[51,52].off 是0,len是2。數(shù)據(jù)流中就只有一個(gè)字節(jié)存在了
if else if的這個(gè)條件判斷發(fā)現(xiàn)都不符合,繼續(xù)往下執(zhí)行。
read()--該方法代表從流中讀取一個(gè)字節(jié),而流中此時(shí)剛好還有一個(gè)字節(jié)存在,該方法執(zhí)行沒(méi)有問(wèn)題。返回值為53
繼續(xù)往下執(zhí)行發(fā)現(xiàn)b[0]=(byte)53.也就是將讀取到的int型轉(zhuǎn)為字節(jié)并存儲(chǔ)在數(shù)組中的第一個(gè)位置,此時(shí)數(shù)組內(nèi)容為[53,52]
繼續(xù)執(zhí)行進(jìn)入for循環(huán),此時(shí)流中已沒(méi)有字節(jié),那么read()方法返回未-1退出循環(huán)。返回變量i的值即是1.
也就是此次方法執(zhí)行讀取了1個(gè)字節(jié)到數(shù)組中。且讀取到了文件的末尾,因此第4次執(zhí)行的時(shí)候到int c=read()方法時(shí)就已經(jīng)返回-1,并沒(méi)有替換數(shù)組中的值了
public int read(byte b[], int off, int len) throws IOException { if (b == null) { throw new NullPointerException(); } else if (off < 0 || len < 0 || len > b.length - off) { throw new IndexOutOfBoundsException(); } else if (len == 0) { return 0; } int c = read(); if (c == -1) { return -1; } b[off] = (byte)c; int i = 1; try { for (; i < len ; i++) { c = read(); if (c == -1) { break; } b[off + i] = (byte)c; } } catch (IOException ee) { } return i; }
讀取過(guò)程圖解:
4. 假設(shè)流中一共有5個(gè)字節(jié),但是我們?cè)O(shè)定的字節(jié)數(shù)組長(zhǎng)度為10,那么讀取幾次?每次情況是怎么樣的?
public class FileStream { public static void main(String[] args) { //建立文件對(duì)象 File file=new File("C:\\Users\\Administrator\\Desktop\\1.txt"); try { //建立鏈接 FileInputStream fileInputStream=new FileInputStream(file); int n=0; byte[] b=new byte[10]; int i=0; while (n!=-1) //當(dāng)n不等于-1,則代表未到末尾 { n=fileInputStream.read(b);//返回實(shí)際讀取到字節(jié)數(shù)組中的字節(jié)數(shù) System.out.println(n); System.out.println(Arrays.toString(b)); //讀取后的字節(jié)數(shù)組內(nèi)容 i++; System.out.println("執(zhí)行次數(shù):"+i); } System.out.println(new String(b)); } catch (FileNotFoundException e) { System.out.println("文件不存在或者文件不可讀或者文件是目錄"); } catch (IOException e) { System.out.println("讀取過(guò)程存在異常"); } } }
執(zhí)行結(jié)果如下:
結(jié)合上面提到的源碼我們可以發(fā)現(xiàn),源碼中的for循環(huán),盡管len是10(數(shù)組長(zhǎng)度),但是當(dāng)i=5時(shí),流中的字節(jié)已經(jīng)讀取完畢,指針移到文件的末尾,因此不會(huì)繼續(xù)執(zhí)行for循環(huán)。并且返回5,剛好符合結(jié)果中第一次實(shí)際讀取5個(gè)字節(jié)到數(shù)組中。第二次讀取時(shí)指針已到末尾。因此int c = read()這里返回-1。就已經(jīng)結(jié)束了方法,并沒(méi)有改變數(shù)組也沒(méi)有再次for循環(huán)
但是這種情況存在一個(gè)問(wèn)題:即數(shù)組中有5個(gè)位置被浪費(fèi)了,并沒(méi)有任何數(shù)據(jù)在里面
具體讀取圖解:
結(jié)合以上兩種情況,那么發(fā)現(xiàn)在使用read(byte b[])方法時(shí)的數(shù)組長(zhǎng)度至關(guān)重要,若長(zhǎng)度小于流的字節(jié)長(zhǎng)度,那么最后得出的內(nèi)容會(huì)出現(xiàn)丟失。若大于流的字節(jié)長(zhǎng)度,那么最后數(shù)組的內(nèi)存就浪費(fèi)了,那么就需要根據(jù)文件的字節(jié)長(zhǎng)度來(lái)設(shè)置數(shù)組的長(zhǎng)度
byte[] b=new byte[(int) file.length()];
3.3)從輸入流中讀取最多l(xiāng)en個(gè)字節(jié)到字節(jié)數(shù)組中(從數(shù)組的off位置開始存儲(chǔ)字節(jié)),當(dāng)len為0時(shí)則返回0,如果len不為零,則該方法將阻塞,直到某些輸入可用為止--此處存疑
public int read(byte[] b,int off,int len) throws IOException
源碼如下
public int read(byte b[], int off, int len) throws IOException { if (b == null) { throw new NullPointerException(); } else if (off < 0 || len < 0 || len > b.length - off) { throw new IndexOutOfBoundsException(); } else if (len == 0) { return 0; } int c = read(); if (c == -1) { return -1; } b[off] = (byte)c; int i = 1; try { for (; i < len ; i++) { c = read(); if (c == -1) { break; } b[off + i] = (byte)c; } } catch (IOException ee) { } return i; }
3.4)關(guān)閉此輸入流并釋放與該流關(guān)聯(lián)的所有系統(tǒng)資源---即釋放與實(shí)際文件的連接(查看源碼可發(fā)現(xiàn)有同步鎖鎖住資源,因此關(guān)閉流釋放鎖)
public void close() throws IOException
三、三種read方法效率比較
1、查看三種read方法源碼,其本質(zhì)都是利用for循環(huán)對(duì)內(nèi)容進(jìn)行單字節(jié)的讀取
2、從代碼形式看,使用read(byte[] b)較為直觀和簡(jiǎn)便,因此項(xiàng)目中可以此方法為主進(jìn)行數(shù)據(jù)讀取
到此這篇關(guān)于Java使用FileInputStream流讀取文件示例詳解的文章就介紹到這了,更多相關(guān)Java FileInputStream流讀取文件內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- java中的FileInputStream三種read()函數(shù)用法
- java中的FileInputStream(輸入流)
- java基礎(chǔ)知識(shí)之FileInputStream流的使用
- Java FileInputStream與FileOutputStream使用詳解
- Java基礎(chǔ)之FileInputStream和FileOutputStream流詳解
- Java FileInputStream讀中文亂碼問(wèn)題解決方案
- Java中的FileInputStream 和 FileOutputStream 介紹_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
- Java中的FileInputStream是否需要close問(wèn)題
相關(guān)文章
基于java實(shí)現(xiàn)簡(jiǎn)單的圖片類別識(shí)別
這篇文章主要為大家詳細(xì)介紹了如何基于java實(shí)現(xiàn)簡(jiǎn)單的圖片類別識(shí)別功能,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-12-12Mybatis中如何設(shè)置sqlSession自動(dòng)提交
在MyBatis中,默認(rèn)情況下,獲取的SqlSession對(duì)象不會(huì)自動(dòng)提交事務(wù),這意味著在進(jìn)行更新、刪除或插入等操作后,需要顯式調(diào)用commit方法來(lái)提交事務(wù),但是,可以在獲取SqlSession時(shí)通過(guò)將openSession方法的參數(shù)設(shè)置為true2024-09-09java中functional interface的分類和使用詳解
這篇文章主要介紹了java中functional interface的分類和使用,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-04-04Java設(shè)計(jì)模式之初識(shí)行為型模式
今天帶大家學(xué)習(xí)Java設(shè)計(jì)模式的相關(guān)知識(shí)點(diǎn),文中對(duì)Java行為型模式做了非常詳細(xì)的介紹及代碼示例,對(duì)正在學(xué)習(xí)java的小伙伴們很有幫助,需要的朋友可以參考下2021-06-06java?ThreadPoolExecutor線程池內(nèi)部處理流程解析
這篇文章主要為大家介紹了java?ThreadPoolExecutor線程池內(nèi)部處理流程解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12