Wie gehe ich einen Strom von Daten intern auf eine C-basierte Anwendung?
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.
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.