功能“节约进步”的问题

时间:2016-09-23 10:50:44

标签: c parsing state

我正在尝试解析一些只提取第n个字段的CSV日志文件(为了速度而忽略其他字段)。当我使用fread大于输入大小的缓冲区大小时,我的函数按预期工作。

问题是当我读取部分输入并尝试继续下次调用该函数时离开的位置。我相信问题在于我如何处理null终止符并设置我的全局变量,但我似乎无法弄明白。

任何有助于理解我做错事的帮助都非常感谢!

代码

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

2 个答案:

答案 0 :(得分:1)

您的代码中至少有两个问题是能够以块的形式读取文件。

首先,so数组是自动的:它没有理由将其值从一次调用保持到其他数组。您应该将其声明为全局(在测试函数之外)或静态。

接下来,只有在找到null时才将本地状态复制到全局状态。但是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;
}

但正如你在评论中所说的那样混合局部和全局变量是容易出错的。看起来您在设计程序结构之前就开始编码,并确定实际需要全局化的内容。你没有,是吗? ; - )

答案 1 :(得分: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;
  }
}