Открытие сжатых файлов для чтения на C без создания временных файлов
Вопрос
У меня есть несколько файлов в формате gzip, которые я хочу прочитать на языке C с помощью fopen и fscanf.Можно ли как-нибудь сделать это без необходимости архивировать файлы во временные файлы?
Спасибо.
Решение
Вы можете использовать libzlib для прямого открытия файлов, заархивированных gzip.
Он также предлагает функцию «gzopen», которая ведет себя аналогично fopen, но работает с файлами, заархивированными в gzip.Однако fscanf, вероятно, не будет работать с таким дескриптором, поскольку он ожидает обычные указатели FILE.
Другие советы
Если popen
это честная игра, вы можете сделать это с помощью fopen
и fscanf
:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main(int argc, char *argv[])
{
const char prefix[] = "zcat ";
const char *arg;
char *cmd;
FILE *in;
char buf[4096];
if (argc != 2) {
fprintf(stderr, "Usage: %s file\n", argv[0]);
return 1;
}
arg = argv[1];
cmd = malloc(sizeof(prefix) + strlen(arg) + 1);
if (!cmd) {
fprintf(stderr, "%s: malloc: %s\n", argv[0], strerror(errno));
return 1;
}
sprintf(cmd, "%s%s", prefix, arg);
in = popen(cmd, "r");
if (!in) {
fprintf(stderr, "%s: popen: %s\n", argv[0], strerror(errno));
return 1;
}
while (fscanf(in, "%s", buf) == 1)
printf("%s: got [%s]\n", argv[0], buf);
if (ferror(in)) {
fprintf(stderr, "%s: fread: %s\n", argv[0], strerror(errno));
return 1;
}
else if (!feof(in)) {
fprintf(stderr, "%s: %s: unconsumed input\n", argv[0], argv[1]);
return 1;
}
return 0;
}
Например:
$ zcat file.gz
Every good boy does fine.
$ ./gzread file.gz
./gzread: got [Every]
./gzread: got [good]
./gzread: got [boy]
./gzread: got [does]
./gzread: got [fine.]
Не использовать
sprintf(cmd, "zcat %s", argv[1]);
popen(cmd,"r");
чтобы открыть файлы .gz.Вместо этого правильно экранируйте argv[1].В противном случае вы можете столкнуться с уязвимостью, особенно когда кто-то вводит аргумент argv[1], например
123;rm -rf /
Уже помогает изменение приведенной выше инструкции на
sprintf(cmd, "zcat \'%s\'",argv[1]);
Вы также можете избежать таких символов, как ' 0', '' ',' ; ' и т. д.
Попытка новичка в gzscanf():
#include <stdio.h>
#include <stdarg.h>
#include <zlib.h>
#define MAXLEN 256
int gzscanf(gzFile *stream, const char *fmt, ...) {
/* read one line from stream (up to newline) and parse with sscanf */
va_list args;
va_start(args, fmt);
int n;
static char buf[MAXLEN];
if (NULL == gzgets(stream, buf, MAXLEN)) {
printf("gzscanf: Failed to read line from gz file.\n");
exit(EXIT_FAILURE);
}
n = vsscanf(buf, fmt, args);
va_end(args);
return n;
}
Вы можете использовать zlib, но вам потребуется заменить вызовы ввода-вывода на специфичные для zlib.
для этого вам придется открыть трубу.Основной поток псевдокода:
create pipe // man pipe
fork // man fork
if (parent) {
close the writing end of the pipe // man 2 close
read from the pipe // man 2 read
} else if (child) {
close the reading end of the pipe // man 2 close
overwrite the file descriptor for stdout with the writing end of the pipe // man dup2
call exec() with gzip and the relevant parameters // man 3 exec
}
Вы можете использовать man
страницы в комментариях для получения более подробной информации о том, как это сделать.
Вы можете использовать zlib и обернуть его обычным указателем файла, таким образом вы можете использовать fscanf, fread и т. д.прозрачно.
FILE *myfopen(const char *path, const char *mode)
{
#ifdef WITH_ZLIB
gzFile *zfp;
/* try gzopen */
zfp = gzopen(path,mode);
if (zfp == NULL)
return fopen(path,mode);
/* open file pointer */
return funopen(zfp,
(int(*)(void*,char*,int))gzread,
(int(*)(void*,const char*,int))gzwrite,
(fpos_t(*)(void*,fpos_t,int))gzseek,
(int(*)(void*))gzclose);
#else
return fopen(path,mode);
#endif
}
Это довольно просто использовать zlib
открыть .gz
файлы.Есть разумное руководство по адресу zlib.net.
Вот краткий пример для начала:
#include <stdio.h>
#include <zlib.h>
int main( int argc, char **argv )
{
// we're reading 2 text lines, and a binary blob from the given file
char line1[1024];
char line2[1024];
int blob[64];
if (argc > 1)
{
const char *filename = argv[1];
gzFile gz_in = gzopen( filename, "rb" ); // same as fopen()
if (gz_in != NULL)
{
if ( gzgets( gz_in, line1, sizeof(line1) ) != NULL ) // same as fgets()
{
if ( gzgets( gz_in, line2, sizeof(line2) ) != NULL )
{
if ( gzfread( blob, sizeof(int), 64, gz_in ) == 64 ) // same as fread()
{
printf("Line1: %s", line1);
printf("Line2: %s", line2);
// ...etc
}
}
}
gzclose(gz_in); // same as fclose()
}
else
{
printf( "Failed to GZ-open [%s]\n", filename );
}
}
return 0;
}
Не забудьте поставить ссылку на zlib
, под UNIX gcc ... -lz