Ouvrir des fichiers gzippés pour les lire en C sans créer de fichiers temporaires

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

  •  10-07-2019
  •  | 
  •  

Question

J'ai quelques fichiers gzippés que je veux lire en C via fopen et fscanf. Est-il possible de le faire sans avoir à graver les fichiers dans des fichiers temporaires?

Merci.

Était-ce utile?

La solution

Vous pouvez utiliser libzlib pour ouvrir directement les fichiers gzippés.

Il propose également un " gzopen " fonction qui se comporte de la même manière que fopen mais fonctionne sur les fichiers gzippés. Cependant, fscanf ne fonctionnerait probablement pas sur un tel descripteur, car il attend les pointeurs FILE normaux.

Autres conseils

Si popen est un jeu juste , vous pouvez le faire avec fopen et 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;
}

Par exemple:

$ 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.]

Ne pas utiliser

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

pour ouvrir les fichiers .gz. Bien échapper à argv [1] à la place. Vous pouvez sinon vous retrouver avec une vulnérabilité, en particulier lorsque certains injectent un argument argv [1] tel que

123;rm -rf /

Cela aide déjà à changer l'instruction ci-dessus en

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

Vous pouvez également vouloir échapper des caractères tels que '\ 0', '\' ',' \; ' etc.

Tentative de débutant sur 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;
}

Vous pouvez utiliser zlib , mais vous devrez remplacer vos appels d'E / S par zlib. -spécifique.

vous devez ouvrir un tuyau pour faire cela. Le flux de base dans le pseudo-code est:

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
}

Vous pouvez utiliser les pages man dans les commentaires pour plus de détails sur la procédure à suivre.

Vous pouvez utiliser zlib et l’envelopper dans un pointeur de fichier normal. De cette façon, vous pouvez utiliser fscanf, fread, etc. de manière transparente.

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
}

Il est assez simple d’utiliser zlib pour ouvrir les fichiers .gz . Il existe un manuel raisonnable sur zlib.net .

Voici un exemple rapide pour vous aider à démarrer:

#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;
}

N'oubliez pas de créer un lien avec zlib sous UNIX gcc ... -lz

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top