2016-09-23 74 views
1

我試圖通過一些CSV日誌文件解析只提取第n場(解僱其他人的速度)。當我使用fread大於輸入大小的緩衝區大小時,我的函數按預期工作。功能'保存進度'的問題

問題是當我讀入部分輸入並嘗試繼續下一次調用該函數時離開的位置。我相信問題在於我如何處理空終止符和設置我的全局變量,但我似乎無法弄清楚。

任何幫助理解我在做什麼錯誤非常感謝!

代碼

#include <stdio.h> 
#include <time.h> 

int gcomc = 0; 
int gpos = 0; 

void test(char *str, int len) 
{ 
    const char *ptr = str; 
      char ch; 
      int i; 
      char so[10]; 
      int comc = gcomc; 
      int pos = gpos; 

    for(i = 0; i < len; i++) 
    { 
     ch = ptr[i]; 

     switch(ch) 
     { 
      case ';': 
       comc++; 
       break; 

      case '\0': 
       gcomc = comc; 
       gpos = pos; 
       break; 

      default: 
       if (comc == 3) { 
        ch = ptr[i]; 
        so[pos++] = ch; 
       } 
       if (comc == 7) { 
        printf(" %s ", so); 
        comc = 0; 
        pos = 0; 
        gcomc = 0; 
        gpos = 0; 
       } 
     } 
    } 

return; 
} 

int main(int argc, char* argv[]) 
{ 

FILE *fin=fopen("test.txt", "rb"); 
    char buffer[100 + 1]; 
    size_t bsz; 

    while((bsz = fread(buffer, sizeof *buffer, 100, fin)) > 0) 
    { 
     buffer[bsz] = '\0'; 
     test(buffer, bsz); 
    } 

return 1; 
} 

輸入

A;B;C;D;E;F;G;H 
I;J;K;L;M;N;O;P 
Q;R;S;T;U;V;W;X 
Y;Z;1;2;3;4;5;6 

具有100(101)緩衝區大小

D L T 2 

輸出用的10(11)緩衝區大小

D P 
Q X 
Segmentation fault (core dumped) 
輸出

編輯: 謝謝你的意見和代碼,我返工我的(而啞寫的)代碼 - 任何進一步的批評是值得歡迎的(建設性或破壞性的,我從中學到所有):

#include <stdio.h> 
#include <time.h> 

void test(char *str, int len); 

int gcomc, gpos = 0; 

void test(char *str, int len) 
{ 
const char *ptr = str; 
      char ch; 
      int i; 
      static char so[10]; 

    for(i = 0; i < len; i++) 
    { 
     ch = ptr[i]; 

     switch(ch) 
     { 
      case ';': 
       gcomc++; 
       break; 

      default: 
       if (gcomc == 3) { 
        ch = ptr[i]; 
        so[gpos++] = ch; 
       } 
       if (gcomc == 7) { 
        so[gpos] = '\0'; /* ensure so is null terminated */ 
        printf(" %s ", so); 
        gcomc = 0; 
        gpos = 0; 
       } 
     } 
    } 

return; 
} 

extern int main() 
{ 
FILE *fin=fopen("test.txt", "rb"); 
    char buffer[10 + 1]; 
    size_t bsz; 

    while((bsz = fread(buffer, sizeof *buffer, sizeof buffer, fin)) > 0) 
{ 
    test(buffer, bsz); 
} 

return 1; 
} 
+0

爲什麼你甚至要將全局變量複製到當地人,反之亦然?爲什麼不直接使用全局變量,如果函數改變狀態(這通常不是最好的想法)。另外,它似乎也應該是一個全球性的(如果你在一個令牌中間讀到的話)? – Lou

+0

我會說這種情況'case'\ 0':'永遠不會發生。 – alk

+0

無法使用顯示的代碼重現UB。 – alk

回答

1

有在你的代碼至少有兩個問題,以便能夠讀取該文件的數據塊。

首先,so數組是自動的:它沒有任何理由將它的值從一個調用保持到其他調用。你應該聲明它是全局的(在測試函數之外)或靜態的。

接下來,只有當您找到空值時,您纔將本地狀態複製到全局狀態。但null位於len位置,並且您剛剛退出循環(for(i = 0; i < len; i++)請注意),所以在下次調用時再次以0,0開始。您應該選擇一種方法來指示緩衝區的結束,或者經過寫一個空標記的長度,但是混合這兩者是容易出錯的。當您使用fread,我的建議是堅持一個長度:

在主要用途:

while((bsz = fread(buffer, sizeof *buffer, sizeof buffer, fin)) > 0) 
{ 
    test(buffer, bsz); 
} 

(這樣,你只寫緩衝區的大小一次)

和測試:

void test(char *str, int len) 
{ 
    const char *ptr = str; 
      char ch; 
      int i; 
      static char so[10]; 
      int comc = gcomc; 
      int pos = gpos; 

    for(i = 0; i < len; i++) 
    { 
     ch = ptr[i]; 

     switch(ch) 
     { 
      case ';': 
       comc++; 
       break; 

      default: 
       if (comc == 3) { 
        ch = ptr[i]; 
        so[pos++] = ch; 
       } 
       if (comc == 7) { 
        so[pos] = '\0'; /* ensure so is null terminated */ 
        printf(" %s ", so); 
        comc = 0; 
        pos = 0; 
        gcomc = 0; 
        gpos = 0; 
       } 
     } 
    } 
    gcomc = comc;   /* store the state to globals */ 
    gpos = pos; 

return; 
} 

但正如你在評論混合局部和全局像這樣是容易出錯。看起來您在設計程序結構並確定實際需要成爲全球性之前就開始編碼了。你沒有,是嗎? ;-)

+0

乾杯塞爾,已經更新了我原來的問題。感謝代碼和解釋:) – ADOctopus

0

test()內部的解析器狀態需要經歷多次調用。你照顧這一部分只會讓這些櫃檯變得全球化。全球化是不好的做法。您還錯過了保存so的狀態(其內容)。

封裝結構中的狀態。

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


#define SO_SIZE (10) 

struct state 
{ 
    size_t comc; 
    size_t pos; 
    char so[SO_SIZE + 1]; /* Add 1 for the 0-terminator. */ 
} 

並將它傳遞給解析器的每次調用(此處爲test())。

調整解析器是這樣的:

int test(struct state * pstate, const char *str, size_t len) 
{ 
    int result = 0; /* be optimistic. */ 
    char ch; 
    size_t i; 

    for (i = 0; i <= len; i++) 
    { 
    ch = str[i]; 

    switch (ch) 
    { 
    case ';': 
     pstate->comc++; 
     break; 

    default: 
     if (pstate->comc == 3) 
     { 
     ch = str[i]; 
     if (SO_SIZE <= pstate->pos) 
     { 
      result = -1; /* overflow */ 
      break; 
     } 

     pstate->so[pstate->pos++] = ch; 
     } 

     if (pstate->comc == 7) 
     { 
     printf(" %s ", pstate->so); 
     pstate->comc = 0; 
     pstate->pos = 0; 
     } 
    } 
    } 

    return result; 
} 

然後調用它像這樣:

#define BUFFER_SIZE (100) 

int main(void) 
{ 
    FILE *fin = fopen("test.txt", "rb"); 
    if (NULL == fin) 
    { 
    perror("fopen() failed"); 
    return EXIT_FAILURE; 
    } 

    { 
    char buffer[BUFFER_SIZE + 1] = {0}; 
    size_t bsz; 
    struct state state = {0}; 
    int result; 

    while (0 < (bsz = fread(buffer, sizeof *buffer, sizeof buffer, fin)) 
     && (0 == result)) 
    { 
     result = test(&state, buffer, bsz); 
    } 

    return result ?EXIT_FAILURE :EXIT_SUCCESS; 
    } 
}