Abrindo arquivos gzipped para leitura em C sem criar arquivos temporários
Pergunta
Eu tenho alguns arquivos gzipped que eu quero ler em C via fopen e fscanf. Existe uma maneira de fazer isso sem ter que gunzip os arquivos para arquivos temporários?
Graças.
Solução
Você pode usar libzlib para abrir os arquivos gzipped diretamente.
Ele também oferece uma função "gzopen" que se comporta semelhante a fopen, mas funciona em arquivos gzipped. No entanto, fscanf provavelmente não trabalho em tais pega um, uma vez que espera ponteiros de arquivo normal.
Outras dicas
Se popen
é um jogo justo, você pode fazê-lo com fopen
e 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 exemplo:
$ 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.]
Não use
sprintf(cmd, "zcat %s", argv[1]);
popen(cmd,"r");
para arquivos .gz abertos. Corretamente escapar argv [1] em vez. De contrário pode acabar com uma vulnerabilidade, especialmente quando alguns injeta um argv argumento [1] como
123;rm -rf /
Ele já ajuda a mudar as instruções acima em
sprintf(cmd, "zcat \'%s\'",argv[1]);
Você também pode querer escapar caracteres como '\ 0', '\'', '\'; etc.
Novato tentativa de 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;
}
Você pode usar zlib , mas vai exigir que você substitua as chamadas de I / O para ser zlib espec�ico.
você tem que abrir um canal para fazer isso. O fluxo básico em pseudo-código é:
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
}
Você pode usar as páginas man
nos comentários para mais detalhes sobre como fazer isso.
Você pode usar zlib e envolvê-la para um ponteiro de arquivo regular, desta forma você pode usar fscanf, fread, etc. 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
}
É muito simples de usar zlib
a arquivos .gz
abertos. Há um longo manual do razoável em zlib.net .
Aqui está um exemplo rápido para você começar:
#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;
}
Lembre-se de ligação com zlib
, sob UNIX gcc ... -lz