我有一个包含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
它,该位置实际上应该为空白。
我究竟做错了什么?
您的代码有几个问题
主要问题是您在中返回了指向自动存储的指针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
参数进行完整性检查。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句