【发布时间】:2014-10-21 08:32:27
【问题描述】:
我正在开发一些代码,这些代码需要能够解压缩大型 gzip 文件(最多 5GB 未压缩)并将它们读入内存。我宁愿对此保持干净,而不是简单地将它们临时解压缩到磁盘,所以我一直在使用 zlib 来尝试完成此操作。我已经让它运行了,大部分情况下。这意味着它适用于我用作输入的 5 个文件中的 4 个。另一个文件在处理过程中给出了 Z_BUF_ERROR,我不想忽略它。
这最初发生在不同的代码中,但最终我把它一直带回到我从 zlib 网页上的 zpipe.c 获得的示例代码,无论我使用什么代码,它都会导致相同的 Z_BUF_ERROR 和只有这个文件。在阅读了几篇关于 Z_BUF_ERROR 的帖子以及阅读了这方面的手册之后,我玩了一段时间的代码。最终,我能够通过更改用于保存膨胀输出的缓冲区的大小来找到一种使其工作的方法。通常在这一点上我会称之为一天,直到它报告另一个文件的错误,但理想情况下,这将是某个时候的生产级代码,我想了解错误是什么,这样我就可以防止它,而不仅仅是暂时修复它。特别是因为 gzip 能够很好地压缩和解压缩文件。
我已经尝试了以下变体:
- 不同的平台:CentOS、OSX
- 不同版本的 zlib:1.2.3、1.2.8(结果相同)
- CHUNK 的值和输出的字节数(完整为 783049330):
- 2000000: 783049330
- 1048576: 783049330
- 1000000: 783049330
- 100000: 783049330
- 30000:248421347
- 25000: 31095404
- 20000: 783049330
- 19000:155821787
- 18000:412613687
- 17000: 55799133
- 16384: 37541674
- 16000:783049330
- 任何大于 4100000 的 CHUNK 都会出错
- 尝试使用大于 CHUNK 的值声明(结果相同)
- 尝试使用 malloc 进行声明(结果相同)
- 尝试使用 gzip 解压缩,然后再次压缩文件,认为 gzip 元数据中可能存在某些问题(结果相同)
- 出于相同目的尝试使用 gzip 压缩文件的单独未压缩版本,但我相信原始 .gz 文件是从这个文件创建的(结果相同)
我可能已经尝试了此列表之外的一些东西,因为我一直试图深入了解它,但只有更改 CHUNK 大小才能使这项工作正常进行。我唯一担心的是我不知道为什么不同的大小会起作用,而且我担心另一个 CHUNK 大小会使其他文件面临这个问题的风险,因为这只是一个文件的问题。
` 代码:
FILE* fp = fopen( argv[1], "rb" );
int ret = inf( fp, stdout );
fclose( fp );
int inf(FILE *source, FILE *dest)
{
size_t CHUNK = 100000;
int count = 0;
int ret;
unsigned have;
z_stream strm;
unsigned char in[CHUNK];
unsigned char out[CHUNK];
char out_str[CHUNK];
/* allocate inflate state */
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
strm.avail_in = 0;
strm.next_in = Z_NULL;
ret = inflateInit2(&strm, 16+MAX_WBITS);
if (ret != Z_OK)
return ret;
/* decompress until deflate stream ends or end of file */
do {
strm.avail_in = fread(in, 1, CHUNK, source);
if (ferror(source)) {
(void)inflateEnd(&strm);
return Z_ERRNO;
}
if (strm.avail_in == 0)
break;
strm.next_in = in;
/* run inflate() on input until output buffer not full */
do {
strm.avail_out = CHUNK;
strm.next_out = out;
ret = inflate(&strm, Z_NO_FLUSH);
switch (ret) {
case Z_NEED_DICT:
ret = Z_DATA_ERROR; /* and fall through */
case Z_DATA_ERROR:
case Z_MEM_ERROR:
(void)inflateEnd(&strm);
return ret;
}
have = CHUNK - strm.avail_out;
char out_str[have+1];
strncpy( out_str, (char*)out, have );
out_str[have] = '\0';
// testing the ability to store the result in a string object and viewing the output
std::cout << "out_str: " << std::string(out_str) << " ::" << std::endl;
if( ret == Z_BUF_ERROR ){
std::cout << "Z_BUF_ERROR!" << std::endl;
exit(1);
}
} while (strm.avail_out == 0);
/* done when inflate() says it's done */
} while (ret != Z_STREAM_END);
/* clean up and return */
(void)inflateEnd(&strm);
return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR;
}
`
【问题讨论】: