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

但是,如果val5缺少第6个字段(),则会val6在Table中的位置处插入val5,该位置实际上应该为空白。

我究竟做错了什么?

chqrlie

您的代码有几个问题

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

  • 关于空值,您不能使用strtokCSV格式进行解析:它首先跳过所有出现的定界符,因此您不能有空字段,因为的序列,将被解释为单个分隔符。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参数进行完整性检查

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章