Java InputStream 读取缓冲区

Java InputStream read buffer

假设我正在尝试像这样从 Java InputStream 读取:

ZipInputStream zis = new ZipInputStream(new FileInputStream("C:\temp\sample3.zip"));
zis.getNextEntry();
byte[] buffer2 = new byte[2];
int count = zis.read(buffer2));
if(count != -1) //process...
else...//something wrong, abort

我正在解析一个二进制文件,在这种情况下我将缓冲区设置为 2,因为我想阅读下一个短片。如果我想读取下一个 int 等其他类型,我会将缓冲区设置为 4。问题是有时 zis.read(buffer) 不会填充缓冲区,即使我知道有足够的未读数据来填充缓冲区。我可以简单地将整个文件内容转储到一个数组中并对其进行解析,但后来我最终实现了自己的流 reader 来做这件事,这似乎是在重新发明轮子。我还可以实现一个 read() 函数来检查读取计数,如果小于 buffersize,则请求更多数据来填充缓冲区,但这是低效且丑陋的。有更好的方法吗?

这是此处发布的问题的后续问题:

您需要检查字节数并继续阅读,直到获得所需的所有信息

zis.getNextEntry();
byte[] buffer2 = new byte[2];
int count = 0;
while (count < 2) {
  int bytesRead = zis.read(buffer2, count, 2 - count));
  if(bytesRead != -1) {
    count += bytesRead;
  }
  else...//something wrong, abort
}
//process...

ZipInputStream 符合InputStream 定义的契约。 read(byte[] ...) 方法被允许并记录为 return -1 表示流的结尾,或者 任何值 在 (1...请求的长度之间).

并且 API 以这种方式定义是有充分理由的,它使实现可以自由地 return 部分数据可用,而不会在等待时长时间阻塞使数据可用(想想 SocketInputStream)。

如果您需要最少的数据,您需要重复调​​用 read,直到您读取了继续处理所需的尽可能多的数据。

至于 "thats inefficient and ugly",通过批量读取方法读取少量数据会产生自己的开销,并且可能在您显示的代码中还会为您读取的每个数据实体创建一个垃圾字节 [] .要读取少量字节,您可以简单地使用 read() 方法,该方法 return 是单个字节,以简单的实用方法实现,例如:

 static short readShort(InputStream in) throws IOException {
      short s = 0;
      for (int i=0; i<2; ++i) {
          int read = in.read();
          if (read < 0)
              throw new IOException("unexpected end of stream");
          s = (short) ((s << 8) | read);
      }
      return s;
 }

(这可以很容易地适应其他原始类型)

单字节 I/O 在大多数情况下是完全可以接受的,只要您注意确保将 InputStream 包装到 BufferedInputStream 中即可。然后平均开销减少到 BufferedInputStream 内部的几个数组索引边界检查。它不会导致对本机数据源的调用过多。

Is there a better way to do this?

嗯...... ZipInputStream 最终继承自 InputStream 所以你应该能够用 BufferedInputStream 包装它然后 DataInputStream 并使用读取数据readShortreadInt 等等。

像这样:

while (zis.getNextEntry() != null) {
  DataInputStream dis = new DataInputStream(new BufferedInputStream(zis));
  boolean done = false;
  do {
    short s = dis.readShort();
    int i = dis.readInt();
    ...
  } while (!done);
}

注意:您不应该关闭 dis 流,因为那样会导致 zis 被关闭。 (显然,zis 需要在外部级别关闭以避免资源泄漏。)

堆栈中的 BufferedInputStream 可确保您不会对底层流进行大量小读取...这很糟糕。

唯一可能的陷阱是它的方法对如何表示二进制数据有特殊的想法;例如数字是 bigendian。如果这是一个问题,请考虑将整个 zip 条目读入字节数组,并将其包装在 ByteBuffer.