Question

Je tire des données à partir d'un flux de bzip2 dans une application C. Comme des morceaux de données sortent du décompresseur, ils peuvent être écrites stdout:

fwrite(buffer, 1, length, stdout);

Cela fonctionne très bien. Je reçois toutes les données quand il est envoyé à stdout.

Au lieu d'écrire à stdout, je voudrais traiter la sortie de cette déclaration en interne dans une ligne-morceaux:. Une chaîne qui se termine par un saut de ligne \n

Dois-je écrire la sortie du flux de décompresseur à un autre tampon, un caractère à la fois, jusqu'à ce que je frappe un saut de ligne, puis appeler la fonction de traitement par ligne? Est-ce lent et est-il une approche plus intelligente? Merci pour vos conseils.

EDIT

Merci pour vos suggestions. Je fini par la création d'une paire de tampons qui stockent le reste (le « talon » à la fin d'un tampon de sortie) au début d'un tampon court de ligne, chaque fois que je passe à travers la valeur de la mémoire tampon de sortie de données.

boucle I par le caractère de la mémoire tampon de sortie de caractère et traiter la valeur d'un saut de ligne en ligne de données à la fois. Le reste de saut de ligne se fait moins affecté et affecté, et copiée dans la mémoire tampon de ligne du flux suivant. Il semble que realloc est moins cher que les déclarations de malloc-free répétées.

Voici le code que je suis venu avec:

char bzBuf[BZBUFMAXLEN];
BZFILE *bzFp;
int bzError, bzNBuf;
char bzLineBuf[BZLINEBUFMAXLEN];
char *bzBufRemainder = NULL;
int bzBufPosition, bzLineBufPosition;

bzFp = BZ2_bzReadOpen(&bzError, *fp, 0, 0, NULL, 0); /* http://www.bzip.org/1.0.5/bzip2-manual-1.0.5.html#bzcompress-init */ 

if (bzError != BZ_OK) {
    BZ2_bzReadClose(&bzError, bzFp);   
    fprintf(stderr, "\n\t[gchr2] - Error: Bzip2 data could not be retrieved\n\n");
    return -1;          
}

bzError = BZ_OK;
bzLineBufPosition = 0;
while (bzError == BZ_OK) {

    bzNBuf = BZ2_bzRead(&bzError, bzFp, bzBuf, sizeof(bzBuf));

    if (bzError == BZ_OK || bzError == BZ_STREAM_END) {
        if (bzBufRemainder != NULL) {
            /* fprintf(stderr, "copying bzBufRemainder to bzLineBuf...\n"); */
            strncpy(bzLineBuf, bzBufRemainder, strlen(bzBufRemainder)); /* leave out \0 */
            bzLineBufPosition = strlen(bzBufRemainder);
        }

        for (bzBufPosition = 0; bzBufPosition < bzNBuf; bzBufPosition++) {
            bzLineBuf[bzLineBufPosition++] = bzBuf[bzBufPosition];
            if (bzBuf[bzBufPosition] == '\n') {
                bzLineBuf[bzLineBufPosition] = '\0'; /* terminate bzLineBuf */

                /* process the line buffer, e.g. print it out or transform it, etc. */
                fprintf(stdout, "%s", bzLineBuf);

                bzLineBufPosition = 0; /* reset line buffer position */
            }
            else if (bzBufPosition == (bzNBuf - 1)) {
                bzLineBuf[bzLineBufPosition] = '\0';
                if (bzBufRemainder != NULL)
                    bzBufRemainder = (char *)realloc(bzBufRemainder, bzLineBufPosition);
                else
                    bzBufRemainder = (char *)malloc(bzLineBufPosition);
                strncpy(bzBufRemainder, bzLineBuf, bzLineBufPosition);
            }
        }
    }
}

if (bzError != BZ_STREAM_END) {
    BZ2_bzReadClose(&bzError, bzFp);
    fprintf(stderr, "\n\t[gchr2] - Error: Bzip2 data could not be uncompressed\n\n");
    return -1;  
} else {   
    BZ2_bzReadGetUnused(&bzError, bzFp, 0, 0);
    BZ2_bzReadClose(&bzError, bzFp);
}

free(bzBufRemainder);
bzBufRemainder = NULL;

J'apprécie vraiment l'aide de tout le monde. Cela fonctionne bien.

Était-ce utile?

La solution

Ce serait facile à faire en utilisant C ++ de std::string de ', mais en C, il prend un peu de code si vous voulez le faire efficacement (sauf si vous utilisez une bibliothèque de chaîne dynamique).

char *bz_read_line(BZFILE *input)
{
    size_t offset = 0;
    size_t len = CHUNK;  // arbitrary
    char *output = (char *)xmalloc(len);
    int bzerror;

    while (BZ2_bzRead(&bzerror, input, output + offset, 1) == 1) {
        if (offset+1 == len) {
            len += CHUNK;
            output = xrealloc(output, len);
        }
        if (output[offset] == '\n')
            break;
        offset++;
    }

    if (output[offset] == '\n')
        output[offset] = '\0';  // strip trailing newline
    else if (bzerror != BZ_STREAM_END) {
        free(output);
        return NULL;
    }

    return output;
}

(où xmalloc et les erreurs de poignée de xrealloc interne. Ne pas oublier de free la chaîne retournée.)

Ceci est presque un ordre de grandeur plus lent que bzcat:

lars@zygmunt:/tmp$ wc foo
 1193  5841 42868 foo
lars@zygmunt:/tmp$ bzip2 foo
lars@zygmunt:/tmp$ time bzcat foo.bz2 > /dev/null

real    0m0.010s
user    0m0.008s
sys     0m0.000s
lars@zygmunt:/tmp$ time ./a.out < foo.bz2 > /dev/null

real    0m0.093s
user    0m0.044s
sys     0m0.020s

A vous de décider si cela est acceptable.

Autres conseils

Je ne pense pas qu'il y ait une approche plus intelligente (à l'exception de trouver une bibliothèque qui fait déjà des automates pour vous). Attention à l'attribution d'une bonne taille pour la « dernière ligne » tampon:. Si elle ne peut pas gérer une longueur arbitraire et l'entrée provient de quelque chose d'accessible à des tiers, il devient un risque de sécurité

J'ai aussi travaillé avec le traitement des données bzip2 par ligne, et je trouve que la lecture d'un octet à la fois était trop lent. Cela a fonctionné mieux pour moi:

#include <stdio.h>
#include <stdlib.h>
#include <bzlib.h>

/* gcc -o bz bz.c -lbz2 */

#define CHUNK 128

struct bzdata {
  FILE *fp;
  BZFILE *bzf;
  int bzeof, bzlen, bzpos;
  char bzbuf[4096];
};

static int bz2_open(struct bzdata *bz, char *file);
static void bz2_close(struct bzdata *bz);
static int bz2_read_line(struct bzdata *bz, char **line, int *li);
static int bz2_buf(struct bzdata *bz, char **line, int *li, int *ll);


static int
bz2_buf(struct bzdata *bz, char **line, int *li, int *ll)
{
  int done = 0;

  for (; bz->bzpos < bz->bzlen && done == 0; bz->bzpos++) {
    if (*ll + 1 >= *li) {
      *li += CHUNK;
      *line = realloc(*line, (*li + 1) * sizeof(*(*line)));
    }
    if ( ((*line)[(*ll)++] = bz->bzbuf[bz->bzpos]) == '\n') {
      done = 1;
    }
  }

  if (bz->bzpos == bz->bzlen) {
    bz->bzpos = bz->bzlen  = 0;
  }

  (*line)[*ll] = '\0';

  return done;
}

static int
bz2_read_line(struct bzdata *bz, char **line, int *li)
{
  int bzerr = BZ_OK, done = 0, ll = 0;

  if (bz->bzpos) {
    done = bz2_buf(bz, line, li, &ll);
  }

  while (done == 0 && bz->bzeof == 0) {
    bz->bzlen = BZ2_bzRead(&bzerr, bz->bzf, bz->bzbuf, sizeof(bz->bzbuf));

    if (bzerr == BZ_OK || bzerr == BZ_STREAM_END) {
      bz->bzpos = 0;

      if (bzerr == BZ_STREAM_END) {
        bz->bzeof = 1;
      }
      done = bz2_buf(bz, line, li, &ll);
    } else { 
      done = -1;
    }
  }

  /* Handle last lines that don't have a line feed */
  if (done == 0 && ll > 0 && bz->bzeof) {
    done = 1;
  }

  return done;
}

static int
bz2_open(struct bzdata *bz, char *file)
{
  int bzerr = BZ_OK;

  if ( (bz->fp = fopen(file, "rb")) &&
       (bz->bzf = BZ2_bzReadOpen(&bzerr, bz->fp, 0, 0, NULL, 0)) &&
       bzerr == BZ_OK) {
    return 1;
  }

  return 0;
}

static void
bz2_close(struct bzdata *bz)
{
  int bzerr;

  if (bz->bzf) {
    BZ2_bzReadClose(&bzerr, bz->bzf);
    bz->bzf = NULL;
  }

  if (bz->fp) {
    fclose(bz->fp);
    bz->fp = NULL;
  }
  bz->bzpos = bz->bzlen = bz->bzeof = 0;
}

int main(int argc, char *argv[]) {
  struct bzdata *bz = NULL;
  int i, lc, li = 0;
  char *line = NULL;

  if (argc < 2) {
    return fprintf(stderr, "usage: %s file [file ...]\n", argv[0]);
  }  

  if ( (bz = calloc(1, sizeof(*bz))) ) {
    for (i = 1; i < argc; i++) {
      if (bz2_open(bz, argv[i])) {
        for (lc = 0; bz2_read_line(bz, &line, &li) > 0; lc++) {
          /* Process line here */
        }
        printf("%s: lines=%d\n", argv[i], lc);
      }
      bz2_close(bz);
    }

    free(bz);
  }

  if (line) {
    free(line);
  }

  return 0;
}

Je pense que vous devez copier des morceaux de caractères à un autre tampon jusqu'à ce que le dernier morceau que vous écrivez contient un caractère de nouvelle ligne. Ensuite, vous pouvez travailler sur toute la ligne.

Vous pouvez enregistrer le reste du tampon (après la '\n') dans un temporaire, puis créer une nouvelle ligne de celui-ci.

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