CSV文件读取问题:包含大量数据

时间:2012-11-02 10:13:49

标签: c file csv file-handling

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

char* getfield(char* line, int num) {
    char* tok = line;
    char* result;
    if (line)
    {
        do
        {
            if (!--num)
            {
                tok = strchr(line, ',');
                if (tok == NULL)
                {
                    tok = &line[strlen(line)];
                }
                size_t fieldlen = tok - line;
                if (fieldlen)
                {
                    result = (char*)malloc(fieldlen+1);
                    result[fieldlen] = '\0';
                    strncpy(result, line, fieldlen);
                    return result;
                }
                else
                {
                    break;
                }
            }
            tok = strchr(line, ',');
            line = tok + 1;
        } while (tok);
    }
    result = (char*)malloc(2);
    strcpy(result, "0");
    return result;
}

int main()
{
    FILE* stream = fopen("data.csv", "r");
    char line[1024];
    char *pstr;int num1,num2,num3;
    char* value1,value2,value3;

    while (fgets(line, 1024, stream))
    {
        char* tmp = strdup(line);

        value1=getfield(tmp, 1);
        value2=getfield(tmp, 2);
        value3=getfield(tmp, 3);

        num1 =strtol(value1,&pstr,10);
        num2 =strtol(value2,&pstr,10);
        num3 =strtol(value3,&pstr,10)
        free(value1); 
        free(value2); 
        free(value3);
        printf("Fields 1,2,3 would be 1=%d 2=%d 3=%d\n", num1,num2,num3);
        // NOTE strtok clobbers tmp
        free(tmp);
    }
}

以上是我的C代码来读取文件....

 :::: data.csv ::::
    10,34,30
    10,33,
    23,45,23
    25,,45

以上是我的档案..

这里我的问题是我可以用“num”字段调用该函数。所以,为了读取每一行,我想把这个函数调用3次.. !!因此对于大型数据文件来说性能太低了。有人可以帮助我,我可以立即调用该函数并返回一个数组...比我可以轻松存储和打印(例如第一行数组[0] = 10,数组[1] = 34,数组[2] = 30)

2 个答案:

答案 0 :(得分:3)

您可以通过创建一个快速split函数来加速它,这将破坏您的line(更不用说潜伏的分段错误和内存泄漏;此代码没有错误检查或{{1资源):

free

结果:

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

char **split(char *line, char sep, int fields) {
  char **r = (char **)malloc(fields * sizeof(char*));

  int lptr = 0, fptr = 0;
  r[fptr++] = line;

  while (line[lptr]) {
    if (line[lptr] == sep) {
      line[lptr] = '\0';
      r[fptr] = &(line[lptr+1]);
      fptr++;
    }

    lptr++;
  }

  return r;
}

int main(int argc, char **argv) {
  char line[] = "some,info,in a line";

  char **fields = split(line, ',', 3);

  printf("0:%s 1:%s 2:%s\n", fields[0], fields[1], fields[2]);
}

答案 1 :(得分:1)

我没有对你的代码进行时序测试,但我敢打赌镍问题是使用malloc()。那很慢。

Bart意味着char []数组可以包含多个字符串,背靠背。如果您将数组扫描为单个字符串一次,将所有','字符更改为'\ 0',您的最后一行将如下所示:

{ '2', '5', 0, 0, '4', '5', 0, ? rest of buffer }
   ^           ^   ^        !

下面的^ carets标记了记录指向三个字符串的指针的位置。如您所见,它们相当于单独数组中单独的“25”,“”,“45”字符串。的!下面标记结束原始字符串的0。除此之外没有任何意义。

所有这些都取决于能够就地修改原始字符串,可能使其无法进行任何进一步处理(如果检测到无效字段,则打印出有问题的行)。但是,您已经在复制原始缓冲区以供本地使用,因此这不应该是一个问题。顺便说一下,我也摆脱了那个复制缓冲区的malloc。

代码可能如下所示:

while (fgets(line, 1024, stream))
{
    char tmp[sizeof line]; /* this will save a malloc()/free() pair */
    char *tok, *fence, *pstr;
    char ch, *cp1=line, *cp2=tmp;

    while (0 != (ch = *cp1++))
        *cp2++ = (ch == ',') ? 0 : ch;

    fence = cp2; /* remember end of string */
    *fence = 0;  /* and terminate final string */
    tok = tmp;   /* point to first token */

    num1 =strtol(tok, &pstr, 10);
    if (tok < fence) tok += strlen(tok) + 1;

    num2 =strtol(tok,&pstr,10);
    if (tok < fence) tok += strlen(tok) + 1;

    num3 =strtol(tok,&pstr,10);

    printf("Fields 1,2,3 would be 1=%d 2=%d 3=%d\n", num1,num2,num3);
}

显然,您不需要1K缓冲区来处理三个值,因此会有一个循环来提取值。前两个strtol()调用之后的if语句是getfield()的替代品,不再需要它。

在此工作之后,查看数据验证。此(或原始)中的任何内容都不会检测到无效数字。