Java普通方式讀寫文件使用字節數組作爲緩衝時的注意事項

這篇文章主要是針對使用普通InputStream和OutputStream讀寫時遇到問題的總結。

直接上代碼:

    String filePathForRead = "D:\\For Testing\\test.txt";
	File fileForRead = new File(filePathForRead);
	if (!fileForRead.exists()) {
		System.out.println("File for reading not exists");
		return;
	}
	InputStream inputStream = new FileInputStream(fileForRead);
	String filePathForWrite = "D:\\For Testing\\test_write.txt";
	File fileForWrite = new File(filePathForWrite);
	OutputStream outputStream = new FileOutputStream(fileForWrite);
    int bufferSize = 4;
	System.out.println("BufferSize : " + bufferSize);
 	byte[] buffer = new byte[bufferSize ];
 	System.out.println("AvailableSize : " + inputStream.available());
	while (inputStream.read(buffer) != -1) {
		outputStream.write(buffer);
	}
	outputStream.close();
	inputStream.close();
	System.out.println("End");

以上述方式讀寫文件的問題顯而易見,即

byte[] buffer = new byte[4];

......

while (inputStream.read(buffer) != -1) {
	outputStream.write(buffer);
}


(爲了方便說明,這裏將緩衝數組大小看作bufferSize, 將可從讀取文件中獲取到的所有字節大小看作:availableSize,availableSize可以通過inputStream.available()方法獲取)

如果availableSize不是bufferSize的整數倍,以outputStream.write(buffer)方式直接往outputstream中寫時,最後一次循環時,緩衝數組中存有髒數據,導致outputStream中有多餘的數據。如下圖:

BufferSize : 4
AvailableSize : 41
End

所要讀取文件中的內容:
test for IO.
test...
test...
testabcde

最後生成的文件中的內容:
test for IO.
test...
test...
testabcdebcd

Buffer數組內容分析:

....

....

倒數第二次:a,b,c,d

最後一次循環:e,b,c,d

最後一次循環中b,c,d這3個是髒數據。

所以應該在每次讀取後清空Buffer數組,如下:

while (inputStream.read(buffer) != -1) {
    outputStream.write(buffer);
    buffer = new byte[bufferSizer];
}

但是這種方式有很大的問題:如果文件很大,字節很多,每次循環都重新分配一個字節數組的話,就會導致內存溢出。所以這是一種極爲不成熟的修改方案。

解決方案:

InputStream.read(byte b[])這個方法:

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

關於這個方法返回值的說明:

* @return     the total number of bytes read into the buffer, or
*             <code>-1</code> if there is no more data because the end of
*             the stream has been reached.

這個方法返回 一個讀取到buffer中字節的總數,如果沒有數據則返回-1。

查看 public int read(byte b[], int off, int len)方法:

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;
    }

這個方法返回的i也確實是記錄的讀取到buffer數組的字節的數量。

再看看outputStream的write(byte b[], int off, int len),將b數組的 off 到 off + len -1 下標的數據寫入到OutputStream

/**
 * Writes len bytes from the specified byte array starting at offset off to this output 
 * stream.The general contract for write(b, off, len) is thatsome of the bytes in the array 
 * b are written to theoutput stream in order; element b[off] is the firstbyte written and 
 * b[off+len-1] is the last byte writtenby this operation. 
 *
 */ 
public void write(byte b[], int off, int len) throws IOException {
        if (b == null) {
            throw new NullPointerException();
        } else if ((off < 0) || (off > b.length) || (len < 0) ||
                   ((off + len) > b.length) || ((off + len) < 0)) {
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return;
        }
        for (int i = 0 ; i < len ; i++) {
            write(b[off + i]);
        }
    }

如下是解決方案代碼:

        String filePathForRead = "D:\\For Testing\\test.txt";
		File fileForRead = new File(filePathForRead);
		if (!fileForRead.exists()) {
			System.out.println("File for reading not exists");
			return;
		}
		InputStream inputStream = new FileInputStream(fileForRead);
		String filePathForWrite = "D:\\For Testing\\test.txt";
		File fileForWrite = new File(filePathForWrite);
		OutputStream outputStream = new FileOutputStream(fileForWrite);
		int bufferSize = 4;
		System.out.println("BufferSize : " + bufferSize);
 		byte[] buffer = new byte[bufferSize];
 		int count = 0;
 		System.out.println("AvailableSize : " + inputStream.available());
		while ((count = inputStream.read(buffer)) != -1) {
			
			outputStream.write(buffer, 0, count);
		}
		outputStream.close();
		inputStream.close();
		
	}

結果:

BufferSize : 4
AvailableSize : 41
End

所要讀取文件中的內容:
test for IO.
test...
test...
testabcde

最後生成的文件中的內容:
test for IO.
test...
test...
testabcde

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章