2016-02-27 34 views
0

我有一个CSV文件有7个领域:CSV分析功能无法识别空值

me,val1,val2,val3,val4,val5,val6, 
me,val1,val2,val3,val4,val5,val6, 

我解析这个使用下面的功能:

void readcsv() { 
    lk_dispclr(); 
    lk_disptext(2, 0, "Parsing CSV..", 0); 
    lk_disptext(3, 0, "Please Wait..", 0); 

    FILE *stream = fopen("input.csv", "r"); 
    if (stream != NULL) { 
     char line[1024]; 
     while (fgets(line, 1024, stream)) { 
      char *tmp = strdup(line);  
      char a1[20] = ""; 
      char b1[20] = ""; 
      char c1[20] = ""; 
      char d1[20] = ""; 
      char e1[20] = ""; 
      char f1[20] = ""; 
      char g1[20] = ""; 

      strcat(a1, getcsvfield(tmp, 1)); 
      strcat(b1, getcsvfield(tmp, 2)); 
      strcat(c1, getcsvfield(tmp, 3)); 
      strcat(d1, getcsvfield(tmp, 4)); 
      strcat(e1, getcsvfield(tmp, 5)); 
      strcat(f1, getcsvfield(tmp, 6)); 
      strcat(g1, getcsvfield(tmp, 7)); 

      //printf("Field 1 would be %s\n", a1); 
      //printf("Field 2 would be %s\n", getcsvfield(tmp, 2)); 
      //printf("Field 2 would be %s\n", getcsvfield(tmp, 3)); 
      //printf("Field 2 would be %s\n", getcsvfield(tmp, 4)); 
      //printf("Field 2 would be %s\n", getcsvfield(tmp, 5)); 
      //printf("Field 2 would be %s\n", getcsvfield(tmp, 6)); 
      execute("INSERT INTO sdata (uid,sid,name,area,type,stbamount,pkgamount)" 
        " VALUES('%s','%s','%s','%s','%s','%s','%s');", 
        a1, b1, c1, d1, e1, f1, g1); 
      // NOTE strtok clobbers tmp 
      free(tmp); 
     } 
     lk_dispclr(); 
     lk_disptext(2, 4, "CSV Imported!", 1); 
     lk_getkey(); 
    } else { 
     lk_dispclr(); 
     lk_disptext(2, 4, "CSV Not Found!", 1); 
     lk_getkey(); 
    } 
} 

//Used for parsing CSV 
const char *getcsvfield(char *line, int num) { 
    char buffer[1024] = { 0 }; 
    strcpy(buffer, line); 
    const char *tok; 
    for (tok = strtok(buffer, ","); 
     tok && *tok; 
     tok = strtok(NULL, ",\n")) 
    { 
     if (!--num) 
      return tok; 
    } 
    return NULL; 
} 

但是,如果第6场( val5)缺少val6插入表中val5的位置,实际上它应该是空白的。

我在做什么错?

+0

在http://stackoverflow.com/questions/32349263/c-regex-how-to-match-any-string-ending-with-or-any-empty-string/32351114#32351114我提供了一个基本的CSV解析器可以满足您的需求。 –

回答

1

你的代码中有几个问题

  • 主要问题是你在getcsvfield返回一个指针自动存储:您复制line到本地阵列buffer和使用strtok解析它。当您返回第n个元素时,tok指向buffer这是一个本地数组。从函数getcsvfield返回后引用该数组将调用未定义的行为。您可以通过将该字段复制到作为参数getcsvfield收到的缓冲区中来解决此问题。

  • 关于空值,则不能使用strtok解析CSV格式:它首先跳过分隔符的所有出现,因此你不能有空白字段的,序列将被解释为一个分隔符。 strtok是一个使用隐藏的全局状态的过时函数,您可能应该避免在其他位置使用它。

这里是一个改进版本:

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

//Used for parsing CSV 
char *getcsvfield(char *dest, int size, const char *line, int num) { 
    const char *p; 

    for (p = line; *p != '\0' && *p != '\n';) { 
     int len = strcspn(p, ",\n"); /* parse field characters */ 
     if (--num <= 0) { 
      if (len >= size) 
       len = size - 1; 
      memcpy(dest, p, len); 
      dest[len] = '\0'; 
      return dest; 
     } 
     p += len; 
     if (*p == ',') 
      p++; 
    } 
    *dest = '\0'; 
    return NULL; 
} 

void readcsv(void) { 
    lk_dispclr(); 
    lk_disptext(2, 0, "Parsing CSV..", 0); 
    lk_disptext(3, 0, "Please Wait..", 0); 

    FILE *stream = fopen("input.csv", "r"); 
    if (stream != NULL) { 
     char line[1024]; 
     while (fgets(line, 1024, stream)) { 
      char a1[20], b1[20], c1[20], d1[20], e1[20], f1[20], g1[20]; 

      getcsvfield(a1, sizeof a1, line, 1); 
      getcsvfield(b1, sizeof b1, line, 2); 
      getcsvfield(c1, sizeof c1, line, 3); 
      getcsvfield(d1, sizeof d1, line, 4); 
      getcsvfield(e1, sizeof e1, line, 5); 
      getcsvfield(f1, sizeof f1, line, 6); 
      getcsvfield(g1, sizeof g1, line, 7); 

      execute("INSERT INTO sdata (uid,sid,name,area,type,stbamount,pkgamount)" 
        " VALUES('%s','%s','%s','%s','%s','%s','%s');", 
        a1, b1, c1, d1, e1, f1, g1); 
     } 
     fclose(stream); 
     lk_dispclr(); 
     lk_disptext(2, 4, "CSV Imported!", 1); 
     lk_getkey(); 
    } else { 
     lk_dispclr(); 
     lk_disptext(2, 4, "CSV Not Found!", 1); 
     lk_getkey(); 
    } 
} 

请注意,您的插入方法可能允许攻击者通过CSV文件进行SQL注入。在上面的例子中,由于每个字段有20个字节的限制,所以这很困难,但在其他地方,编写SQL命令时应该更加小心。 SQlite也可能会对execute参数进行完整性检查。

+0

非常感谢您的答复。对于最近的答复,我很忙。我了解它是一个指针和strtok调用的问题。关于SQL注入..我明白..但这是为了一个嵌入式设备,将由授权个人携带。我目前没有访问该设备..我会尽力回复明天:) – techno

+0

我已经接受..非常感谢:) – techno

+0

空值仍然被替换为CSV中的下一个条目。 – techno