Abrir archivos comprimidos para leer en C sin crear archivos temporales
Pregunta
Tengo algunos archivos comprimidos que quiero leer en C a través de fopen y fscanf. ¿Hay alguna forma de hacer esto sin tener que comprimir los archivos a archivos temporales?
Gracias.
Solución
Puede usar libzlib para abrir los archivos comprimidos directamente.
También ofrece un "gzopen" función que se comporta de manera similar a fopen pero opera en archivos comprimidos. Sin embargo, fscanf probablemente no funcionaría en un controlador de este tipo, ya que espera punteros de ARCHIVO normales.
Otros consejos
Si popen
es un juego justo , puede hacerlo con fopen
y 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;
}
Por ejemplo:
$ 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.]
No usar
sprintf(cmd, "zcat %s", argv[1]);
popen(cmd,"r");
para abrir archivos .gz. En su lugar, escapa de argv [1] correctamente. De lo contrario, puede terminar con una vulnerabilidad, especialmente cuando algunos inyectan un argumento argv [1] como
123;rm -rf /
Ya ayuda cambiar las instrucciones anteriores a
sprintf(cmd, "zcat \'%s\'",argv[1]);
También es posible que desee escapar de caracteres como '\ 0', '\' ',' \; ' etc.
Intento de novato en 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;
}
Puede usar zlib , pero requerirá que reemplace sus llamadas de E / S para ser zlib -específico.
tienes que abrir una tubería para hacer esto. El flujo básico en pseudocódigo es:
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
}
Puede usar las páginas man
en los comentarios para obtener más detalles sobre cómo hacerlo.
Puede usar zlib y ajustarlo a un puntero de archivo normal, de esta manera puede usar fscanf, fread, etc. transparentemente.
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
}
Es bastante simple usar zlib
para abrir archivos .gz
. Hay un manual razonable en zlib.net .
Aquí hay un ejemplo rápido para comenzar:
#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;
}
Recuerde vincular con zlib
, en UNIX gcc ... -lz