Открытие GZIP-файлов для чтения в C без создания временных файлов - PullRequest
8 голосов
/ 30 ноября 2009

У меня есть несколько сжатых файлов, которые я хочу прочитать на С через fopen и fscanf. Есть ли способ сделать это без необходимости разархивировать файлы во временные файлы?

Спасибо.

Ответы [ 8 ]

6 голосов
/ 30 ноября 2009

Если 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.]
6 голосов
/ 30 ноября 2009

Вы можете использовать libzlib для непосредственного открытия сжатых файлов.

Он также предлагает функцию "gzopen", которая ведет себя подобно fopen, но работает с файлами gzipped. Однако, fscanf, вероятно, не будет работать с таким дескриптором, так как он ожидает обычных указателей FILE.

4 голосов
/ 05 января 2016

Не использовать

sprintf(cmd, "zcat %s", argv[1]);
popen(cmd,"r");

, чтобы открыть .gz файлы. Правильно избегайте argv [1]. В противном случае вы можете получить уязвимость, особенно когда некоторые вводят аргумент argv [1], такой как

123;rm -rf /

Уже помогает изменить приведенную выше инструкцию на

sprintf(cmd, "zcat \'%s\'",argv[1]);

Вы также можете экранировать такие символы, как '\ 0', '\' ',' \; ' и т.д.

1 голос
/ 12 ноября 2010

Попытка новичка в 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;
}
0 голосов
/ 29 ноября 2018

Довольно просто использовать 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

0 голосов
/ 13 февраля 2014

Вы можете использовать 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
}
0 голосов
/ 30 ноября 2009

Вы должны открыть трубу, чтобы сделать это. Основной поток в псевдокоде:

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 в комментариях для более подробной информации о том, как это сделать.

0 голосов
/ 30 ноября 2009

Вы можете использовать zlib , но для этого потребуется, чтобы вы заменили ваши вызовы ввода-вывода, чтобы они были специфичными для zlib.

...