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

问题描述 投票:0回答:2

我尝试像这样解析一行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
c csv scanf
2个回答
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:原理也是被称为Postel定律的Jon Postel,在早期的TCP规范中写道:TCP实现应该遵循健全性的一般原则:保守你做什么,对你接受别人的做法要自由。)

您还可以设计一个基于strcspn()的方案来识别逗号之间的字符。您将每个字段处理为一个字符串,然后在适当的时候将字符串转换为数字(并验证数字:负分数,分数超过100等可能无效)。这是最灵活的方案。它还可以保护您免受整数溢出的影响,sscanf()没有。


0
投票

在调用middleName = NULL;之前,你不应该做sscanf()。您需要提供一个指向内存的有效指针来保存中间名。如果该行有一个空的中间名,它将用空字符串填充它。

首先不需要逐个字符地读取行。让sscanf()完成它的工作。

© www.soinside.com 2019 - 2024. All rights reserved.