我尝试像这样解析一行csv文件
47369758,Ysabel,Rosalie,Matthewson,41,76,47,42,70,83
69054587,Errick,Clareta,,34,67,57,43,27,49
95926740,Gottfried,Farr,Sampson,95,100,61,46,2,85
格式为id,firstName,lastname,middlename,hw1,hw2,hw3,midterm1,midterm2,final。并且可能没有中间名,我如何使用sscanf正确读取第二行。
我首先使用getline逐行读取文件,然后将行放入sscanf中进行解析以获取正确的相应值,并通过char读取char以查看该行是否继续使用逗号,如果有两个逗号则使用sscanf而不使用中间名
char *line = NULL;
size_t len = 1000;
while(getline(&line, &len, stdin)!= EOF)
{
int idNum, final;
char* firstName = malloc(100);
char* lastName =malloc(100);
char* middleName =malloc(100);
int hw1, hw2, hw3;
int m1, m2;
Student * student = malloc(sizeof(Student));
student->m_scores = malloc(sizeof(Midterms));
int i;
int counter =0;
for (i=0; i< strlen(line); i++){
if(line[i] == ',' && line[i+1] == ',')
{counter++;}
}
printf("counter: %d\n", counter);
if (counter == 1)
{
sscanf(line,"%d ,%[^,],%[^,],%0[^,],%d ,%d,%d,%d,%d,%d\n",&idNum, firstName, lastName,middleName, &hw1, &hw2, &hw3, &m1, &m2, &final);
}
else{
sscanf(line,"%d ,%[^,],%[^,], %[^,],%d ,%d,%d,%d,%d,%d\n",&idNum, firstName, lastName, middleName, &hw1, &hw2, &hw3, &m1, &m2, &final);
}
这是我的代码,读取没有中间名称的行
sscanf(line,"%d ,%[^,],%[^,],%d ,%d,%d,%d,%d,%d\n",&idNum, firstName, lastName, &hw1, &hw2, &hw3, &m1, &m2, &final);
这是我的代码,用中间名读取行
sscanf(line,"%d ,%[^,],%[^,], %[^,],%d ,%d,%d,%d,%d,%d\n",&idNum, firstName, lastName, middleName, &hw1, &hw2, &hw3, &m1, &m2, &final);
这是我的实际结果
47369758,Ysabel,Rosalie,Matthewson,41,76,47,42,70,83
69054587,Errick,Clareta,,41,76,47,42,70,83
95926740,Gottfried,Farr,Sampson,95,100,61,46,2,85
这是预期的结果
47369758,Ysabel,Rosalie,Matthewson,41,76,47,42,70,83
69054587,Errick,Clareta,,34,67,57,43,27,49
95926740,Gottfried,Farr,Sampson,95,100,61,46,2,85
无论如何,您需要检查sscanf()
返回的值。使用行计数器的诀窍本质上是脆弱的,不能扩展以处理数十名学生,更不用说数百或数千或更多。你需要做的更像是:
int rc = sscanf(line, "%d , %[^,], %[^,], %[^,],%d ,%d ,%d ,%d ,%d ,%d",
&idNum, firstName, lastName, middleName, &hw1, &hw2, &hw3, &m1, &m2, &final);
if (rc == 10)
{
/* All present and correct */
}
else if (rc == 3)
{
/* Problem at middle name — presumably it is missing */
rc = sscanf(line, "%d , %[^,], %[^,],,%d ,%d ,%d ,%d ,%d ,%d",
&idNum, firstName, lastName, &hw1, &hw2, &hw3, &m1, &m2, &final);
if (rc != 9)
{
/* Misformatted still — there is an irresolvable problem with this line */
}
else
{
/* All except middle name present and correct */
middlename[0] = '\0';
}
}
else
{
/* Misformatted — there is an irresolvable problem with this line */
}
/* Process information here — unless you did it at the 'present and correct' lines */
如果存在无法解决的问题,您可以报告错误,引用整行(如果您不使用getline()
加上sscanf()
则无法执行此操作 - 这就是为什么建议使用该组合)。
如果没有无法解决的问题,您可以继续将数据复制到最近分配的结构中。您可以决定是否处理更多返回代码,对数据应用适当的更正。请记住,sscanf()
在第一次失败时停止解析。
所有基于字符串的输入都应该受到限制 - 因为你似乎分配了100个字节,你应该使用%99[^,]
。您可以考虑是否允许名称包含空格 - 如果不允许,您可以使用%99[, ]
甚至%99[^, \t\n]
或类似名称(您可以考虑在扫描集和后面的逗号之间添加一个空格,以便名称后面的任何尾随空格都是没有扫描(就像扫描集之前的空间跳过名称之前的任何空格。可以说,这会让处理格式错误的数据。这不是一件坏事。(这是Postel定律,或者Robustness Principle:原理也是被称为Postel定律的Jon Postel,在早期的TCP规范中写道:TCP实现应该遵循健全性的一般原则:保守你做什么,对你接受别人的做法要自由。)
您还可以设计一个基于strcspn()
的方案来识别逗号之间的字符。您将每个字段处理为一个字符串,然后在适当的时候将字符串转换为数字(并验证数字:负分数,分数超过100等可能无效)。这是最灵活的方案。它还可以保护您免受整数溢出的影响,sscanf()
没有。
在调用middleName = NULL;
之前,你不应该做sscanf()
。您需要提供一个指向内存的有效指针来保存中间名。如果该行有一个空的中间名,它将用空字符串填充它。
首先不需要逐个字符地读取行。让sscanf()
完成它的工作。