如何使用sscanf以连续逗号读取一行csv

时间:2019-04-27 00:50:45

标签: c csv scanf

我尝试解析这样的一行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

2 个答案:

答案 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()不会。