1

假设我正在尝试从这样的 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) 不会填充缓冲区,即使我知道有足够的未读数据来填充缓冲区。我可以简单地将整个文件内容转储到一个数组中并解析它,但最终我实现了我自己的流阅读器来做这似乎是在重新发明轮子。我还可以实现一个 read() 函数来检查读取计数,如果小于缓冲区大小,则请求更多数据来填充缓冲区,但这既低效又丑陋。有一个更好的方法吗?

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

Java ZipInputStream 提取错误

4

3 回答 3

1

有一个更好的方法吗?

好吧... aZipInputStream最终继承自,InputStream因此您应该能够用 a 包装它,BufferedInputStream然后用 a 包装它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.

于 2017-04-20T14:51:51.100 回答
0

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

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...
于 2017-04-20T14:08:02.720 回答
0

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

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

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

至于“那是低效和丑陋的”,通过批量读取方法读取少量数据会产生其自身的开销,并且可能在您显示的代码中还为您读取的每个数据实体创建一个垃圾字节 []。要读取少量字节,您可以简单地使用返回单个字节的 read() 方法,在一个简单的实用程序方法中实现,例如:

 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 内的几个数组索引边界检查。它不会导致对本机数据源的过多调用。

于 2017-04-20T14:32:55.287 回答