我尝试解析这样的一行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来查看该行是否连续出现逗号(如果有两个逗号)没有中间名的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
答案 0 :(得分:0)
在致电middleName = NULL;
之前,您应该不做sscanf()
。您需要提供指向内存的有效指针以保存中间名。如果该行的中间名为空,则会在其中填充一个空字符串。
无需先逐行读取行。只需让sscanf()
做好工作即可。
答案 1 :(得分:0)
您仍然需要检查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:在Jon Postel(在早期的TCP规范中写道)之后,该原理也称为Postel定律:TCP实现应遵循鲁棒性的一般原则:对您的操作要保守一些,在您接受他人的态度上保持自由。)
您还可以设计基于strcspn()
的方案,以识别逗号之间的字符。您将每个字段处理为一个字符串,然后在适当时将字符串转换为数字(并验证数字:负分数,超过100的分数等,可能无效)。这是最灵活的方案。它还可以保护您免受整数溢出的影响,而sscanf()
不会。