Ouvrir des fichiers gzippés pour les lire en C sans créer de fichiers temporaires
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.
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