Frage

Ich bin Ziehen von Daten aus einem bzip2 Strom innerhalb einer C-Anwendung. Als Datenblocks des Dekompressor kommen, können sie auf stdout geschrieben werden:

fwrite(buffer, 1, length, stdout);

Dies funktioniert gut. Ich bekomme alle die Daten, wenn es um stdout gesendet wird.

Statt zu stdout des Schreibens, ich möchte die Ausgabe von dieser Aussage in einzeilige-Chunks intern verarbeiten. Eine Zeichenfolge, die mit einem Newline-Zeichen \n beendet wird

Muss ich schreiben die Ausgabe des Dekompressor Strom zu einem anderen Puffer, ein Zeichen in einer Zeit, bis ich eine neue Zeile getroffen, und rufen Sie dann die pro-line Funktionsverarbeitung? Ist dies langsam und gibt es eine intelligentere Ansatz? Vielen Dank für Ihre Beratung.

Bearbeiten

Vielen Dank für Ihre Anregungen. I ended ein Paar von Puffern zu schaffen, auf die sich um den Rest zu speichern (der „Stub“ am Ende eines Ausgangspuffers) zu Beginn eines Kurzzeilenpuffer, jedes Mal, wenn ich durch den Ausgangspuffer im Wert übergeben von Daten.

I-Schleife durch den Ausgangspuffer und Zeichen für Zeichen verarbeiten Wert von Daten zu einem Zeitpunkt einer Zeilenumbruch-line ist. Der Newline-less Rest wird zugeteilt und zugewiesen, und auf die nächsten Stream des Zeilenpuffer kopiert. Es scheint, wie realloc ist weniger teuer als wiederholte malloc-free Aussagen.

Hier ist der Code, den ich kam mit:

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;

Ich schätze die Hilfe aller wirklich. Das funktioniert gut.

War es hilfreich?

Lösung

Dies wäre einfach, C ++ 's std::string zu tun mit, aber in C dauert es einige Code, wenn Sie es effizient machen wollen (es sei denn Sie eine dynamische String-Bibliothek verwenden).

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

(Wo xmalloc und xrealloc Griff Fehler intern. Vergessen Sie nicht, den zurückgegebenen String free.)

Das ist fast eine Größenordnung langsamer als 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

Entscheiden Sie selbst, ob das akzeptabel.

Andere Tipps

Ich glaube nicht, dass es ein intelligenter Ansatz (mit Ausnahme eines Automaten Bibliothek zu finden, das bereits erledigt das für Sie). Seien Sie vorsichtig mit dem richtigen Größe für die „letzte Zeile“ Zuweisung Puffer:., Wenn es nicht beliebige Länge verarbeiten kann und die Eingabe kommt von etwas Dritten zugänglich, es wird zu einem Sicherheitsrisiko

Ich habe auch mit der Verarbeitung bzip2 Daten pro Zeile gearbeitet, und ich fand, dass das Lesen eines Byte zu einem Zeitpunkt, zu langsam war. Das funktionierte besser für mich:

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

Ich glaube, Sie Stücke von Zeichen in einen anderen Puffer kopieren sollte, bis der letzte Brocken Sie schreiben eine neue Zeile Zeichen enthält. Dann können Sie auf der ganzen Linie arbeiten.

Sie können den Rest des Puffers speichern (nach dem '\n') in eine temporäre und dann eine neue Zeile daraus erstellen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top