2016-09-23 2 views
1

Я пытаюсь проанализировать некоторые файлы журнала CSV, извлекая только поле n'th (отклонение других для скорости). Моя функция работает так, как ожидалось, когда я использую размер буфера с размером 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) 

Edit: Спасибо за комментарии и код, я переработал мой (довольно тупой письменный) код - дальше критика приветствуется (конструктивная или деструктивная, я узнал от всего этого):

#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

Почему вы даже копируете глобальные таблицы для местных жителей и наоборот? Почему бы не использовать глобальные переменные напрямую, если функция мутирует состояние (что обычно не является лучшей идеей). Кроме того, кажется, что 'so' также должен быть глобальным (что, если вы читаете в середине токена)? – Lou

+0

Я бы сказал, что этот случай 'case '\ 0':' никогда не произойдет. – alk

+0

Невозможно воспроизвести UB с показанным кодом. – alk

ответ

1

Есть по крайней мере две проблемы в вашем коде, чтобы иметь возможность прочитать файл на куски.

Во-первых, массив so является автоматическим: у него нет причин сохранять свои значения от одного вызова до других. Вы должны объявить его глобальным (вне тестовой функции) или статическим.

Затем вы скопируете локальное состояние в глобальное, когда найдете нуль. Но нуль находится в позиции 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

Cheers Serge, обновил мой оригинальный вопрос. Спасибо за код и объяснение :) – 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; 
    } 
} 
Смежные вопросы