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

 

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