FileInputStream.read(byte[])有什么问题?

9
作为我对文件读取问题的回答的回应,一位评论者表示FileInputStream.read(byte[])不能保证填满缓冲区。
File file = /* ... */  
long len = file.length();
byte[] buffer = new byte[(int)len];
FileInputStream in = new FileInputStream(file);
in.read(buffer);

(该代码假设文件长度不超过2GB)
除了IOException,还有什么原因会导致read方法无法检索整个文件内容?
编辑:
代码的思路(以及我回答的问题的OP的目标)是将整个文件一次性读入内存块中,这就是为什么buffer_size = file_size。

如果目标是在一次调用中读取整个文件,我建议重用或创建一个为您执行此操作的库,该库可能在后台使用多个“read”调用。 - Mark Peters
6个回答

6
除了IOException,还有什么原因会导致read方法无法检索整个文件内容?
在我的API实现和自己编写的文件系统中,我只选择填充一半的缓冲区...... 只是开玩笑。
我的观点是,即使我没有开玩笑,从技术上讲,这也不是一个错误。这是方法合同的问题。在这种情况下,合同(文档)如下:
从此输入流中读取最多b.length字节的数据到字节数组中。
也就是说,它不能保证填满缓冲区。
根据API实现的情况,以及可能是文件系统的情况,read方法可能选择不填充缓冲区。这基本上是方法合同所说的问题。
底线:它可能有效,但不能保证有效。

+1 - 这是合同,而不是特定的实现。即使FileInputStream提供了保证,尽可能多地使用InputStream抽象也是有用的,而该抽象适当地也缺乏保证。 - Andy Thomas

6
如果文件在文件系统上是分段的,底层实现知道它将不得不等待硬盘寻找下一个片段(相对于CPU操作来说需要很长时间),那么read()调用返回部分未填充的缓冲区是有意义的,以便应用程序有机会使用已接收到的数据。现在我不知道是否有任何实现是这样工作的,但重点是您不能依赖缓冲区被填充,因为API合同不保证这一点。

3
首先,你把自己置于了一个错误的二分法之中。一个很正常的情况是,由于文件中没有那么多字节,缓冲区不会被填满。这并不是一个IOException,但这并不意味着整个文件的内容没有被读取。
规范说明该方法将返回-1表示流结束,或者阻塞直到至少读取一个字节。实现InputStream的人可以根据需要进行优化(例如,TCP流可以在数据包进来后立即返回数据,而不管调用者选择的缓冲区大小)。FileInputStream可能会填充缓冲区的一块数据。作为调用者,你除了等待该方法返回-1之外,无从得知。
编辑
实际上,在你的例子中,唯一可能出现缓冲区不被填满的情况(使用标准实现)是,如果在你分配缓冲区之后但开始读取之前,文件的大小发生了变化。由于你还没有锁定文件,这是有可能的。

但是……我专门分配了一个与文件长度完全相同的缓冲区。如果文件长达85000字节,如果我分配一个85000字节的数组,read方法难道不会完全填满它吗?(这是针对原始二进制文件的) - Tony the Pony
@Jen 请查看我的编辑,很抱歉没有按照你分配缓冲区的方式进行。实现方式真的取决于具体情况,但我猜测好的实现方式应该会填满整个缓冲区。你绝不能忽略read函数的返回值。 - Mark Peters

2

人们曾经把FileInputStream的读取看作是假设不会填满缓冲区。实际上,在某些情况下,这是一种现实:

  • 如果在“/dev/tty”或命名管道上打开FileInputStream,则read将仅返回当前可用的数据。其他设备文件可能也会出现这种情况。(这些文件可能会返回0L作为文件大小。)

  • 如果文件系统已使用direct_io选项挂载,或者使用相应标志打开文件,则可以实现FUSE文件系统以不完全填充读取缓冲区。

上述内容适用于Linux,但其他操作系统和/或Java实现可能存在类似情况。关键是javadoc允许这种行为,并且如果您的应用程序假定不会发生这种行为,那么可能会遇到麻烦。

有第三方库实现了“完全读取”行为;例如,Apache Commons提供了FileUtils.readFileToByteArrayIOUtils.toByteArray等方法。如果您需要这种行为,则应使用其中之一库,或自己实现它。


1

不能保证填充缓冲区。

文件大小可能小于缓冲区,或者文件剩余部分可能小于缓冲区。


1
但是...我特意分配了一个与文件长度完全相同的缓冲区。如果文件长85000字节,如果我分配一个85000字节的数组,read方法不会完全填充它吗?(这是针对原始二进制文件的) - Tony the Pony

0

你的问题自相矛盾。即使在没有任何想象得到的情况下,也不能保证它会读取整个缓冲区。因此,没有保证,你不能假设它会这样做。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接