Открытие сжатых файлов для чтения на C без создания временных файлов

StackOverflow https://stackoverflow.com/questions/1820144

  •  10-07-2019
  •  | 
  •  

Вопрос

У меня есть несколько файлов в формате 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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top