2010-10-13 20 views
4

我正在從C應用程序中的bzip2流中提取數據。由於數據塊出來解壓的,他們可以寫入stdout如何處理基於C的應用程序內部的數據流?

fwrite(buffer, 1, length, stdout); 

這個偉大的工程。當它被髮送到stdout時,我得到所有的數據。

而不是寫入stdout,我想在單行塊內部處理這個語句的輸出:一個以換行符\n終止的字符串。

我是否將解壓器流的輸出寫入另一個緩衝區,一次一個字符,直到我點擊一個換行符,然後調用per-line處理函數?這是否緩慢,是否有更明智的做法?謝謝你的建議。

編輯

感謝您的建議。我最終創建了一對緩衝區,在每次通過輸出緩衝區的數據時,在短行緩衝區的開始處存儲剩餘部分(輸出緩衝區末尾的「stub」)。

我一個接一個地循環輸出緩衝區,一次處理換行符的數據。無換行符的餘數被分配並分配,並被複制到下一個流的行緩衝區。看起來好像realloc比重複malloc-free陳述便宜。

這是我想出了代碼:

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; 

我真的很感謝大家的幫助。這很好。

+0

strtok可以節省您檢查緩衝區中每個字符的換行符。 – Tumas 2010-10-13 11:30:13

+0

'strtok'就是我以前試過的,但它不適用於我,而且我失去了數據,因爲從bzip2解壓縮器中輸出的輸出緩衝區被分割成一行。最後我失去了那一點。也許我錯誤地使用了它。有沒有使用'strtok'來保留無換行符的「存根」,將那個存根粘貼到下一個輸出緩衝塊的開始處? – 2010-10-13 11:33:04

+0

不,你說得對,strtok不太適合這裏。如果你使用strtok,那麼你會失去這一點,並且可能更容易檢查每個字符。正如Opera所描述的那樣,我不知道有什麼聰明的方式來保存這個不需要新行的「存根」,以備以後使用,除非手動完成。對不起,誤導你一下:) – Tumas 2010-10-13 11:56:39

回答

1

使用C++的std::string可以很容易地完成,但是在C語言中,如果您想高效地執行它(除非使用動態字符串庫),則需要一些代碼。

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

(。凡xmalloc和內部xrealloc手柄錯誤不要忘記free返回的字符串)

這幾乎是一個比bzcat幅度慢的順序:

[email protected]:/tmp$ wc foo 
1193 5841 42868 foo 
[email protected]:/tmp$ bzip2 foo 
[email protected]:/tmp$ time bzcat foo.bz2 > /dev/null 

real 0m0.010s 
user 0m0.008s 
sys  0m0.000s 
[email protected]:/tmp$ time ./a.out <foo.bz2> /dev/null 

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

決定對於你自己而言是否可以接受。

+0

我有一堆bz2流連接在一個非常大的文件。我正在嘗試編寫一個自包含的應用程序來解壓其中一個流。這非常有幫助,謝謝! – 2010-10-13 18:03:28

2

我不認爲有一個更聰明的方法(除了找到一個已經爲你做這個自動機庫)。請謹慎爲「最後一行」緩衝區分配適當的大小:如果無法處理任意長度並且輸入來自第三方可訪問的內容,則會成爲安全風險。

0

我認爲你應該將字符塊複製到另一個緩衝區,直到你寫入的最後一個塊包含一個新的行字符。然後你可以在整條線上工作。

您可以將緩衝區的其餘部分(在'\n'之後)保存到臨時文件中,然後從中創建一個新行。

2

我也一直在處理每行bzip2數據,我發現一次讀一個字節太慢了。這對我更好:

#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; 
} 
相關問題