为啥 InputStream 没有完全填充数组?
Posted
技术标签:
【中文标题】为啥 InputStream 没有完全填充数组?【英文标题】:Why doesn't InputStream fill the array fully?为什么 InputStream 没有完全填充数组? 【发布时间】:2014-09-07 14:32:24 【问题描述】:老兄,我正在使用以下代码来读取一个大文件(2MB 或更多)并处理一些数据业务。 我必须为每个数据读取调用读取 128Byte。 一开始我用这个代码(没问题,效果很好)。
InputStream is;//= something...
int read=-1;
byte[] buff=new byte[128];
while(true)
for(int idx=0;idx<128;idx++)
read=is.read(); if(read==-1)return;//end of stream
buff[idx]=(byte)read;
process_data(buff);
然后我尝试了出现问题的代码(错误!有时反应很奇怪)
InputStream is;//= something...
int read=-1;
byte[] buff=new byte[128];
while(true)
//ERROR! java doesn't read 128 bytes while it's available
if((read=is.read(buff,0,128))==128)process_data(buff);elsereturn;
上面的代码并不是一直有效,我确定数据的数量是可用的,但有时会读取(read
) 127 或 125 或 123。什么问题?
我还找到了一个使用 DataInputStream#readFully(buff:byte[]):void
的代码,它也可以工作,但我只是想知道为什么秒解决方案在数据可用时不填充数组数据。
谢谢朋友。
【问题讨论】:
只是一个旁注:空白不花费 nuthin',但糟糕的代码易读性可能会花费很多。考虑将每个代码语句放在自己的行上。它至少值得这么多。 您知道,使用第二个代码块,您会丢弃数据。如果由于某种原因没有读取 128 个字节,例如,您已经到达数据的末尾,并且只读取了部分缓冲区,如果该缓冲区被丢弃。我自己,我会使用 BufferedInputStream。 API里都有,请阅读。 【参考方案1】:咨询FileInputStream
的javadoc(我假设您正在从文件中读取):
从此输入流中读取最多 len 个字节的数据到一个字节数组中。如果 len 不为零,则该方法会阻塞,直到某些输入可用;否则,不读取任何字节并返回 0。
这里的关键是该方法仅在一些数据可用之前阻塞。返回的值会告诉您实际读取了多少字节。您读取的字节数可能少于 128 字节的原因可能是驱动器/实现定义的行为缓慢。
要获得正确的读取顺序,您应该检查 read()
不等于 -1
(流结束)并写入缓冲区,直到读取到正确数量的数据。
正确实现代码的示例:
InputStream is; // = something...
int read;
int read_total;
byte[] buf = new byte[128];
// Infinite loop
while(true)
read_total = 0;
// Repeatedly perform reads until break or end of stream, offsetting at last read position in array
while((read = is.read(buf, read_total, buf.length - offset)) != -1)
// Gets the amount read and adds it to a read_total variable.
read_total = read_total + read;
// Break if it read_total is buffer length (128)
if(read_total == buf.length)
break;
if(read_total != buf.length)
// Incomplete read before 128 bytes
else
process_data(buf);
编辑:
不要尝试使用 available()
作为数据可用性的指标(我知道这听起来很奇怪),再次使用 javadoc:
返回对可以从此输入流读取(或跳过)的剩余字节数的估计值,而不会被下一次调用此输入流的方法阻塞。当文件位置超出 EOF 时返回 0。下一次调用可能是同一个线程或另一个线程。单次读取或跳过这么多字节不会阻塞,但可能会读取或跳过更少的字节。
在某些情况下,非阻塞读取(或跳过)可能会在其速度较慢时被阻止,例如通过慢速网络读取大文件时。
这里的关键是估计,不要使用估计。
【讨论】:
非常感谢,但我还使用InputStream#available():int
检查了数据可用性,它返回文件大小(在任何读取调用之前),那么现在呢?这里数据可用性的目的是什么?
完美的兄弟,你能解释一下你提供的代码吗?我的第一种方法是烂还是坏?有什么区别? DataInputStream#readFully(buf:byte[]):void
和你提供的一样吗?谢谢老兄
@user2889419 在代码中添加了 cmets。 DataInputStream
出于不同的目的做了完全不同的事情,但查看 readFully() 方法的文档,我相信它会做类似的事情(如果不一样的话)(对于上面的例子)。尽管我会避免使用它并坚持我的示例来保持代码清洁。 (使用 DataInputStream 从文件中读取字节似乎真的很模糊,并且可能有一些缺点 [我目前不知道])。【参考方案2】:
自从提供了接受的答案后,一个新的选项变得可用。从 Java 9 开始,InputStream
类有两个名为 readNBytes
的方法,它们消除了程序员编写读取循环的需要,例如,您的方法可能看起来像
public static void some_method( ) throws IOException
InputStream is = new FileInputStream(args[1]);
byte[] buff = new byte[128];
while (true)
int numRead = is.readNBytes(buff, 0, buff.length);
if (numRead == 0)
break;
// The last read before end-of-stream may read fewer than 128 bytes.
process_data(buff, numRead);
或者稍微简单一点的
public static void some_method( ) throws IOException
InputStream is = new FileInputStream(args[1]);
while (true)
byte[] buff = is.readNBytes(128);
if (buff.length == 0)
break;
// The last read before end-of-stream may read fewer than 128 bytes.
process_data(buff);
【讨论】:
以上是关于为啥 InputStream 没有完全填充数组?的主要内容,如果未能解决你的问题,请参考以下文章
为啥 mongoosastic 填充/弹性搜索没有填充我的参考资料之一?我得到一个空对象