如何在 C 中顺序解析 gzip 压缩的文本文件而不先完全解压缩
How to sequentially parse in C a gzip-compressed text file without fully decompressing it first
我有相当大的文本文件 (~1Gb),其中包含我希望解析的顺序数据(即要读取的行,从上到下)。这些文本文件以 gzip 格式压缩。
目前,我解析这些文件的基本实现(我是 zlib 的新手,多年来没有用 C 编写过)是:
- 使用 zlib 库解压缩文件并将其写入磁盘 (!)
- 从磁盘 (!) 中读取解压后的文本文件并逐行解析
希望,一旦我了解如何更好地使用 zlib(感谢提示 ;-)),这可以得到改进:
- 使用 zlib 库解压缩文件并将内容保存在内存中
- 读取文件(从内存中)并逐行解析
不过,我认为这可以进一步优化,以便在解压缩时解析文件 "online"。我相信 gzip 解压是按顺序进行的,因此可以读取 gzip 文件,并在解压一行文本后立即将其发送到解析器?这样可以避免两次扫描文件,也可能避免将解压缩的文件保留在内存中。
这是一个 answer that says it is possible and preferable 方法。你能告诉我如何实现(或使用实现的库)这样的程序吗?
谢谢,
泰普
您可以通过 popen
打开一个 gzip 压缩文件并从流中顺序读取,就像它是未压缩的一样,除非您不能在流中搜索。
这是一些代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv) {
char buffer[4096];
char *cmd;
int cmdsize;
FILE *fp;
int found = 0;
if (argc < 3) {
printf("usage: zgrep string file\n");
return 2;
}
cmdsize = strlen("gunzip < ") + strlen(argv[2]) + 1;
cmd = malloc(cmdsize);
snprintf(cmd, cmdsize, "gunzip < %s", argv[2]);
if ((fp = popen(cmd, "r")) == NULL) {
perror("cannot run gunzip");
return 1;
}
while (fgets(buffer, sizeof buffer, fp)) {
if (strstr(buffer, argv[1])) {
fputs(buffer, stdout);
found = 1;
}
}
fclose(fp);
return found;
}
是的。您甚至不必使用 popen()
即可; zlib 包含一组函数来执行此操作:
#include <zlib.h>
gzFile fh = gzopen("file.gz", "rb");
char buf[1024];
char *line;
while ((line = gzgets(fh, buf, sizeof(buf)) != NULL) {
// process line
}
gzclose(fh);
同样的界面还支持一行一行写入gzip文件;有关详细信息,请参阅文档。
我有相当大的文本文件 (~1Gb),其中包含我希望解析的顺序数据(即要读取的行,从上到下)。这些文本文件以 gzip 格式压缩。
目前,我解析这些文件的基本实现(我是 zlib 的新手,多年来没有用 C 编写过)是:
- 使用 zlib 库解压缩文件并将其写入磁盘 (!)
- 从磁盘 (!) 中读取解压后的文本文件并逐行解析
希望,一旦我了解如何更好地使用 zlib(感谢提示 ;-)),这可以得到改进:
- 使用 zlib 库解压缩文件并将内容保存在内存中
- 读取文件(从内存中)并逐行解析
不过,我认为这可以进一步优化,以便在解压缩时解析文件 "online"。我相信 gzip 解压是按顺序进行的,因此可以读取 gzip 文件,并在解压一行文本后立即将其发送到解析器?这样可以避免两次扫描文件,也可能避免将解压缩的文件保留在内存中。
这是一个 answer that says it is possible and preferable 方法。你能告诉我如何实现(或使用实现的库)这样的程序吗?
谢谢,
泰普
您可以通过 popen
打开一个 gzip 压缩文件并从流中顺序读取,就像它是未压缩的一样,除非您不能在流中搜索。
这是一些代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv) {
char buffer[4096];
char *cmd;
int cmdsize;
FILE *fp;
int found = 0;
if (argc < 3) {
printf("usage: zgrep string file\n");
return 2;
}
cmdsize = strlen("gunzip < ") + strlen(argv[2]) + 1;
cmd = malloc(cmdsize);
snprintf(cmd, cmdsize, "gunzip < %s", argv[2]);
if ((fp = popen(cmd, "r")) == NULL) {
perror("cannot run gunzip");
return 1;
}
while (fgets(buffer, sizeof buffer, fp)) {
if (strstr(buffer, argv[1])) {
fputs(buffer, stdout);
found = 1;
}
}
fclose(fp);
return found;
}
是的。您甚至不必使用 popen()
即可; zlib 包含一组函数来执行此操作:
#include <zlib.h>
gzFile fh = gzopen("file.gz", "rb");
char buf[1024];
char *line;
while ((line = gzgets(fh, buf, sizeof(buf)) != NULL) {
// process line
}
gzclose(fh);
同样的界面还支持一行一行写入gzip文件;有关详细信息,请参阅文档。