欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java中的字節(jié)流文件讀取教程(一)

 更新時(shí)間:2018年07月03日 09:43:15   作者:Single_Yam  
這篇文章主要給大家介紹了關(guān)于Java中字節(jié)流文件讀取的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用java具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

前言

上篇文章我們介紹了抽象化磁盤(pán)文件的 File 類(lèi)型,它僅僅用于抽象化描述一個(gè)磁盤(pán)文件或目錄,卻不具備訪問(wèn)和修改一個(gè)文件內(nèi)容的能力。

Java 的 IO 流就是用于讀寫(xiě)文件內(nèi)容的一種設(shè)計(jì),它能完成將磁盤(pán)文件內(nèi)容輸出到內(nèi)存或者是將內(nèi)存數(shù)據(jù)輸出到磁盤(pán)文件的數(shù)據(jù)傳輸工作。

Java IO 流的設(shè)計(jì)并不是完美的,設(shè)計(jì)了大量的類(lèi),增加了我們對(duì)于 IO 流的理解,但無(wú)外乎為兩大類(lèi),一類(lèi)是針對(duì)二進(jìn)制文件的字節(jié)流,另一類(lèi)是針對(duì)文本文件的字符流。而本篇我們就先來(lái)學(xué)習(xí)有關(guān)字節(jié)流的相關(guān)類(lèi)型的原理以及使用場(chǎng)景等細(xì)節(jié),主要涉及的具體流類(lèi)型如下:

基類(lèi)字節(jié)流 Input/OutputStream

InputStream 和 OutputStream 分別作為讀字節(jié)流和寫(xiě)字節(jié)流的基類(lèi),所有字節(jié)相關(guān)的流都必然繼承自他們中任意一個(gè),而它們本身作為一個(gè)抽象類(lèi),也定義了最基本的讀寫(xiě)操作,我們一起來(lái)看看:

以 InputStream 為例:

public abstract int read() throws IOException;

這是一個(gè)抽象的方法,并沒(méi)有提供默認(rèn)實(shí)現(xiàn),要求子類(lèi)必須實(shí)現(xiàn)。而這個(gè)方法的作用就是為你返回當(dāng)前文件的下一個(gè)字節(jié)。

當(dāng)然,你也會(huì)發(fā)現(xiàn)這個(gè)方法的返回值是使用的整型類(lèi)型「int」來(lái)接收的,為什么不用「byte」?

首先,read 方法返回的值一定是一個(gè)八位的二進(jìn)制,而一個(gè)八位的二進(jìn)制可以取值的值區(qū)間為:「0000 0000,1111 1111」,也就是范圍 [-128,127]。

read 方法同時(shí)又規(guī)定當(dāng)讀取到文件的末尾,即文件沒(méi)有下一個(gè)字節(jié)供讀取了,將返回值 -1 。所以如果使用 byte 作為返回值類(lèi)型,那么當(dāng)方法返回一個(gè) -1 ,我們?cè)撆卸ㄟ@是文件中數(shù)據(jù)內(nèi)容,還是流的末尾呢?

而 int 類(lèi)型占四個(gè)字節(jié),高位的三個(gè)字節(jié)全部為 0,我們只使用它的最低位字節(jié),當(dāng)遇到流結(jié)尾標(biāo)志時(shí),返回四個(gè)字節(jié)表示的 -1(32 個(gè) 1),這就自然的和表示數(shù)據(jù)的值 -1(24 個(gè) 0 + 8 個(gè) 1)區(qū)別開(kāi)來(lái)了。

接下來(lái)也是一個(gè) read 方法,但是 InputStream 提供默認(rèn)實(shí)現(xiàn):

public int read(byte b[]) throws IOException {
 return read(b, 0, b.length);
}

public int read(byte b[], int off, int len) throws IOException{
 //為了不使篇幅過(guò)長(zhǎng),方法體大家可自行查看 jdk 源碼
}

這兩個(gè)方法本質(zhì)上是一樣的,第一個(gè)方法是第二個(gè)方法的特殊形態(tài),它允許傳入一個(gè)字節(jié)數(shù)組,并要求程序?qū)⑽募凶x到的字節(jié)從數(shù)組索引位置 0 開(kāi)始填充,供填充數(shù)組長(zhǎng)度個(gè)字節(jié)數(shù)。

而第二個(gè)方法更加寬泛一點(diǎn),它允許你指定起始位置和字節(jié)總數(shù)。

InputStream 中還有其他幾個(gè)方法,基本都沒(méi)怎么具體實(shí)現(xiàn),留待子類(lèi)實(shí)現(xiàn),我們簡(jiǎn)單看看。

  • public long skip(long n):跳過(guò) n 個(gè)字節(jié),返回實(shí)際跳過(guò)的字節(jié)數(shù)
  • public void close():關(guān)閉流并釋放對(duì)應(yīng)的資源
  • public synchronized void mark(int readlimit)
  • public synchronized void reset()
  • public boolean markSupported()

mark 方法會(huì)在當(dāng)前流讀取位置打上一個(gè)標(biāo)志,reset 方法即重置讀取指針到該標(biāo)志處。

事實(shí)上,文件讀取是不可能重置回頭讀取的,而一般都是將標(biāo)志位置到重置點(diǎn)之間所有的字節(jié)臨時(shí)保存了,當(dāng)調(diào)用 reset 方法時(shí),其實(shí)是從保存的臨時(shí)字節(jié)集合進(jìn)行重復(fù)讀取,所以 readlimit 用于限制最大緩存容量。

而 markSupported 方法則用于確定當(dāng)前流是否支持這種「回退式」讀取操作。

OutputStream 和 InputStream 是類(lèi)似的,只不過(guò)一個(gè)是寫(xiě)一個(gè)是讀,此處我們不再贅述了。

文件字節(jié)流 FileInput/OutputStream

我們依然著重點(diǎn)于 FileInputStream,而 FileOutputStream 是類(lèi)似的。

首先 FileInputStream 有以下幾種構(gòu)造器實(shí)例化一個(gè)對(duì)象:

public FileInputStream(String name) throws FileNotFoundException {
 this(name != null ? new File(name) : null);
}
public FileInputStream(File file) throws FileNotFoundException {
 String name = (file != null ? file.getPath() : null);
 SecurityManager security = System.getSecurityManager();
 if (security != null) {
 security.checkRead(name);
 }
 if (name == null) {
 throw new NullPointerException();
 }
 if (file.isInvalid()) {
 throw new FileNotFoundException("Invalid file path");
 }
 fd = new FileDescriptor();
 fd.attach(this);
 path = name;
 open(name);
}

這兩個(gè)構(gòu)造器本質(zhì)上也是一樣的,前者是后者的特殊形態(tài)。其實(shí)你別看后者的方法體一大堆代碼,大部分都只是在做安全校驗(yàn),核心的就是一個(gè) open 方法,用于打開(kāi)一個(gè)文件。

主要是這兩種構(gòu)造器,如果文件不存在或者文件路徑和名稱(chēng)不合法,都將拋出 FileNotFoundException 異常。

記得我們說(shuō)過(guò),基類(lèi) InputStream 中有一個(gè)抽象方法 read 要求所有子類(lèi)進(jìn)行實(shí)現(xiàn),而 FileInputStream 使用本地方法進(jìn)行了實(shí)現(xiàn):

public int read() throws IOException {
 return read0();
}

private native int read0() throws IOException;

這個(gè) read0 的具體實(shí)現(xiàn)我們暫時(shí)無(wú)從探究,但是你必須明確的是,這個(gè) read 方法的作用,它用于返回流中下一個(gè)字節(jié),返回 -1 說(shuō)明讀取到文件末尾,已無(wú)字節(jié)可讀。

除此之外,F(xiàn)ileInputStream 中還有一些其他的讀取相關(guān)方法,但大多采用了本地方法進(jìn)行了實(shí)現(xiàn),此處我們簡(jiǎn)單看看:

  • public int read(byte b[]):讀取 b.length() 個(gè)長(zhǎng)度的字節(jié)到數(shù)組中
  • public int read(byte b[], int off, int len):讀取指定長(zhǎng)度的字節(jié)數(shù)到數(shù)組中
  • public native long skip(long n):跳過(guò) n 的字節(jié)進(jìn)行讀取
  • public void close():釋放流資源

FileInputStream 的內(nèi)部方法基本就這么些,還有一些高級(jí)的復(fù)雜的,我們暫時(shí)用不到,以后再進(jìn)行學(xué)習(xí),下面我們簡(jiǎn)單看一個(gè)文件讀取的例子:

public static void main(String[] args) throws IOException {
 FileInputStream input = new FileInputStream("C:\\Users\\yanga\\Desktop\\test.txt");
 byte[] buffer = new byte[1024];
 int len = input.read(buffer);
 String str = new String(buffer);
 System.out.println(str);
 System.out.println(len);
 input.close();
}

輸出結(jié)果很簡(jiǎn)單,會(huì)打印出我們 test 文件中的內(nèi)容和實(shí)際讀出的字節(jié)數(shù),但細(xì)心的同學(xué)就會(huì)發(fā)現(xiàn)了,你怎么就能保證 test 文件中內(nèi)容不會(huì)超過(guò) 1024 個(gè)字節(jié)呢?

為了能夠完整的讀出文件中的內(nèi)容,一種解決辦法是:將 buffer 定義的足夠大,以期望盡可能的能夠存儲(chǔ)下文件中的所有內(nèi)容。

這種方法顯然是不可取的,因?yàn)槲覀兏静豢赡軐?shí)現(xiàn)知道待讀文件的實(shí)際大小,一味的創(chuàng)建過(guò)大的字節(jié)數(shù)組其本身也是一種很差勁的方案。

第二種方式就是使用我們的動(dòng)態(tài)字節(jié)數(shù)組流,它可以動(dòng)態(tài)調(diào)整內(nèi)部字節(jié)數(shù)組的大小,保證適當(dāng)?shù)娜萘?,這一點(diǎn)我們后文中將詳細(xì)介紹。

關(guān)于 FileOutputStream,還需要強(qiáng)調(diào)一點(diǎn)的是它的構(gòu)造器,其中有以下兩個(gè)構(gòu)造器:

public FileOutputStream(String name, boolean append)

public FileOutputStream(File file, boolean append)

參數(shù) append 指明了,此流的寫(xiě)入操作是覆蓋還是追加,true 表示追加,false 表示覆蓋。

字節(jié)數(shù)組流 ByteArrayInput/OutputStream

所謂的「字節(jié)數(shù)組流」就是圍繞一個(gè)字節(jié)數(shù)組運(yùn)作的流,它并不像其他流一樣,針對(duì)文件進(jìn)行流的讀寫(xiě)操作。

字節(jié)數(shù)組流雖然并不是基于文件的流,但卻依然是一個(gè)很重要的流,因?yàn)樗鼉?nèi)部封裝的字節(jié)數(shù)組并不是固定的,而是動(dòng)態(tài)可擴(kuò)容的,往往基于某些場(chǎng)景下,非常合適。

ByteArrayInputStream 是讀字節(jié)數(shù)組流,可以通過(guò)以下構(gòu)造函數(shù)被實(shí)例化:

protected byte buf[];
protected int pos;
protected int count;

public ByteArrayInputStream(byte buf[]) {
 this.buf = buf;
 this.pos = 0;
 this.count = buf.length;
}

public ByteArrayInputStream(byte buf[], int offset, int length)

buf 就是被封裝在 ByteArrayInputStream 內(nèi)部的一個(gè)字節(jié)數(shù)組,ByteArrayInputStream 的所有讀操作都是圍繞著它進(jìn)行的。

所以,實(shí)例化一個(gè) ByteArrayInputStream 對(duì)象的時(shí)候,至少傳入一個(gè)目標(biāo)字節(jié)數(shù)組的。

pos 屬性用于記錄當(dāng)前流讀取的位置,count 記錄了目標(biāo)字節(jié)數(shù)組最后一個(gè)有效字節(jié)索引的后一個(gè)位置。

理解了這一點(diǎn),有關(guān)它各種的 read 方法就不難了:

//讀取下一個(gè)字節(jié)
public synchronized int read() {
 return (pos < count) ? (buf[pos++] & 0xff) : -1;
}
//讀取 len 個(gè)字節(jié)放到字節(jié)數(shù)組 b 中
public synchronized int read(byte b[], int off, int len){
 //同樣的,方法體較長(zhǎng),大家查看自己的 jdk
}

除此之外,ByteArrayInputStream 還非常簡(jiǎn)單的實(shí)現(xiàn)了「重復(fù)讀取」操作。

public void mark(int readAheadLimit) {
 mark = pos;
}

public synchronized void reset() {
 pos = mark;
}

因?yàn)?ByteArrayInputStream 是基于字節(jié)數(shù)組的,所有重復(fù)讀取操作的實(shí)現(xiàn)就比較容易了,基于索引實(shí)現(xiàn)就可以了。

ByteArrayOutputStream 是寫(xiě)的字節(jié)數(shù)組流,很多實(shí)現(xiàn)還是很有自己的特點(diǎn)的,我們一起來(lái)看看。

首先,這兩個(gè)屬性是必須的:

protected byte buf[];

//這里的 count 表示的是 buf 中有效字節(jié)個(gè)個(gè)數(shù)
protected int count;

構(gòu)造器:

public ByteArrayOutputStream() {
 this(32);
}
 
public ByteArrayOutputStream(int size) {
 if (size < 0) {
 throw new IllegalArgumentException("Negative initial size: "+ size);
 }
 buf = new byte[size];
}

構(gòu)造器的核心任務(wù)是,初始化內(nèi)部的字節(jié)數(shù)組 buf,允許你傳入 size 顯式限制初始化的字節(jié)數(shù)組大小,否則將默認(rèn)長(zhǎng)度 32 。

從外部向 ByteArrayOutputStream 寫(xiě)內(nèi)容:

public synchronized void write(int b) {
 ensureCapacity(count + 1);
 buf[count] = (byte) b;
 count += 1;
}

public synchronized void write(byte b[], int off, int len){
 if ((off < 0) || (off > b.length) || (len < 0) ||
 ((off + len) - b.length > 0)) {
 throw new IndexOutOfBoundsException();
 }
 ensureCapacity(count + len);
 System.arraycopy(b, off, buf, count, len);
 count += len;
}

看到?jīng)]有,所有寫(xiě)操作的第一步都是 ensureCapacity 方法的調(diào)用,目的是為了確保當(dāng)前流內(nèi)的字節(jié)數(shù)組能容納本次寫(xiě)操作。

而這個(gè)方法也很有意思了,如果計(jì)算后發(fā)現(xiàn),內(nèi)部的 buf 不能夠支持本次寫(xiě)操作,則會(huì)調(diào)用 grow 方法做一次擴(kuò)容。擴(kuò)容的原理和 ArrayList 的實(shí)現(xiàn)是類(lèi)似的,擴(kuò)大為原來(lái)的兩倍容量。

除此之外,ByteArrayOutputStream 還有一個(gè) writeTo 方法:

public synchronized void writeTo(OutputStream out) throws IOException {
 out.write(buf, 0, count);
}

將我們內(nèi)部封裝的字節(jié)數(shù)組寫(xiě)到某個(gè)輸出流當(dāng)中。

剩余的一些方法也很常用:

  • public synchronized byte toByteArray()[]:返回內(nèi)部封裝的字節(jié)數(shù)組
  • public synchronized int size():返回 buf 的有效字節(jié)數(shù)
  • public synchronized String toString():返回該數(shù)組對(duì)應(yīng)的字符串形式

注意到,這兩個(gè)流雖然被稱(chēng)作「流」,但是它們本質(zhì)上并沒(méi)有像真正的流一樣去分配一些資源,所以我們無(wú)需調(diào)用它的 close 方法,調(diào)了也沒(méi)用(人家官方說(shuō)了,has no effect)。

測(cè)試的案例就不放出來(lái)了,等會(huì)我會(huì)上傳本篇文章用到的所有代碼案例,大家自行選擇下載即可。

為了控制篇幅,余下流的學(xué)習(xí),放在下篇文章。

文章中的所有代碼、圖片、文件都云存儲(chǔ)在我的 GitHub 上:

(https://github.com/SingleYam/overview_java)

大家也可以選擇通過(guò)本地下載。

總結(jié)

以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。

相關(guān)文章

  • 使用ftpClient下載ftp上所有文件解析

    使用ftpClient下載ftp上所有文件解析

    最近項(xiàng)目需要寫(xiě)個(gè)小功能,需求就是實(shí)時(shí)下載ftp指定文件夾下的所有文件(包括子目錄)到本地文件夾中,保留文件到目錄路徑不變。今天小編給大家分享使用ftpClient下載ftp上所有文件的方法,需要的的朋友參考下吧
    2017-04-04
  • springboot themaleaf 第一次進(jìn)頁(yè)面不加載css的問(wèn)題

    springboot themaleaf 第一次進(jìn)頁(yè)面不加載css的問(wèn)題

    這篇文章主要介紹了springboot themaleaf 第一次進(jìn)頁(yè)面不加載css的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-10-10
  • Java8 將一個(gè)List<T>轉(zhuǎn)為Map<String,T>的操作

    Java8 將一個(gè)List<T>轉(zhuǎn)為Map<String,T>的操作

    這篇文章主要介紹了Java8 將一個(gè)List<T>轉(zhuǎn)為Map<String, T>的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-02-02
  • SWT JFace 拖曳效果

    SWT JFace 拖曳效果

    SWT(JFace)體驗(yàn)之拖曳效果
    2009-06-06
  • Java向上轉(zhuǎn)型和向下轉(zhuǎn)型的區(qū)別說(shuō)明

    Java向上轉(zhuǎn)型和向下轉(zhuǎn)型的區(qū)別說(shuō)明

    這篇文章主要介紹了Java向上轉(zhuǎn)型和向下轉(zhuǎn)型的區(qū)別說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • JavaWeb文件下載功能實(shí)例代碼

    JavaWeb文件下載功能實(shí)例代碼

    這篇文章主要為大家詳細(xì)介紹了JavaWeb文件下載功能實(shí)例代碼,代碼簡(jiǎn)單實(shí)用,感興趣的小伙伴們可以參考一下
    2016-06-06
  • ManyToMany單向、雙向:@JoinTable的使用

    ManyToMany單向、雙向:@JoinTable的使用

    這篇文章主要介紹了ManyToMany單向、雙向:@JoinTable的使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • Spring案例打印機(jī)的實(shí)現(xiàn)過(guò)程詳解

    Spring案例打印機(jī)的實(shí)現(xiàn)過(guò)程詳解

    這篇文章主要介紹了Spring案例打印機(jī)的實(shí)現(xiàn)過(guò)程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-10-10
  • 關(guān)于Integer.parseInt()方法的使用

    關(guān)于Integer.parseInt()方法的使用

    這篇文章主要介紹了關(guān)于Integer.parseInt()方法的使用,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-11-11
  • Springboot項(xiàng)目快速實(shí)現(xiàn)Aop功能

    Springboot項(xiàng)目快速實(shí)現(xiàn)Aop功能

    這篇文章主要介紹了Springboot項(xiàng)目如何快速實(shí)現(xiàn)Aop功能,對(duì)此方面感興趣的小伙伴可以詳細(xì)參考閱讀本文,本文有一定的參考價(jià)值
    2023-03-03

最新評(píng)論