(读取CSV文件)scanf跳转到模式不匹配后的下一行输入

时间:2013-09-18 03:06:17

标签: c csv

如果scanf遇到不匹配,我想给出错误消息然后继续读取其余输入。如果其中一个输入格式不正确,我当前的代码将进入无限循环。

预期输入:

101,Betty,Hay,123 Main St.,23
234,Sheila,Benn,937 Ash Ave.,46
384,Ron,Jewell,3991 Cedar Ave.,30
291,Bill,Read,83 Crescent St.,19
103,Alexander,Ott,100 2nd St.,21
115,Tricia,Munson,585 Oxen Ave.,28
118,Sam,Munson,585 Oxen Ave.,35
110,Valerie,Parker,1513 Copper Rd.,37

代码:

#include <stdio.h>

int main()
{
    int sum = 0;
    int count = 0;
    while (1)
    {
        int id;
        char first[80], last[80], addr[80];
        int age;
        scanf("%d,%80[^,],%80[^,],%80[^,],%d", &id, first, last, addr, &age);
        if (feof(stdin)) break;
        printf("id=%d first=%s last=%s addr=%s age=%d\n",
               id, first, last, addr, age);
        sum += age;
        count++;
    }

    printf("Average age is %f\n", (float)sum / count);

    return 0;
}

我尝试通过将scanf放在if语句中将其与预期的读取次数进行比较来解决这个问题,这有助于显示错误消息,但无助于读取其余的输入。有没有办法跳到下一行输入?

3 个答案:

答案 0 :(得分:2)

您需要直接从scanf()测试结果。在此上下文(以及大多数其他内容)中,我建议您使用fgets()sscanf()而不是普通scanf()

#include <stdio.h>

int main(void)
{
    int sum = 0;
    int count = 0;
    int lineno = 0;
    char line[4096];
    while (fgets(line, sizeof(line), stdin) != 0)
    {
        int id;
        char first[80], last[80], addr[80];
        int age;
        lineno++;
        if (sscanf(line, "%d,%79[^,],%79[^,],%79[^,],%d", &id, first, last, addr, &age) != 5)
            fprintf(stderr, "Invalid data in line %d:\n%s", lineno, line);
        else
        {
            printf("id=%d first=%s last=%s addr=%s age=%d\n",
                   id, first, last, addr, age);
            sum += age;
            count++;
        }
    }

    printf("Average age is %f\n", (float)sum / count);

    return 0;
}

其中的修复包括:

  1. 直接检查输入功能(fgets()) - 使用feof()通常不合适。
  2. 检查sscanf()调用是否找到5个值。
  3. 确保字符串不会溢出分配的缓冲区; sscanf()等需要在结尾处排除空字节的大小 - 因此格式为80到79的变化。
  4. 能够报告整个有缺陷的行,因为它全部被读入。而scanf()代替了它,它已经选择了行的一个不确定的初始部分,留下谁知道剩下哪些报告错误。在这里,您获得了整条线,并且可以更有意义地报告错误。我添加了一个行号来进一步改进。

答案 1 :(得分:1)

scanf()无法阅读时,它会在失败的地方停止。你需要忽略其余部分。

一种方法是逐个阅读字符,直到看到换行符。您可以使用getc()功能。您也可以使用fgets()一次性读取其余内容。然后使用scanf()继续。

答案 2 :(得分:1)

出于这个原因,使用scanf不是最佳选择。我建议使用fgetsstdin中的整行文本捕获到您的角色数组中。然后使用strtok标记字符串并处理每个输入。

也许是这样的:

#include <stdio.h>
#include <string.h>

int main()
{
    int sum = 0;
    int count = 0;
    while (1)
    {
        int id;
        char line[240], * token;
        char first[80], last[80], addr[80];
        int age;

        if (fgets(line,240,stdin) == NULL) break;
        line[strlen(line) - 1] = '\0'; /* remove \n */
        id = atoi(strtok(line,","));
        strcpy(first,strtok(NULL,","));
        strcpy(last,strtok(NULL,","));
        strcpy(addr,strtok(NULL,","));
        age = atoi(strtok(NULL,","));
        printf("id=%d first=%s last=%s addr=%s age=%d\n",
               id,first, last, addr, age);
        sum += age;
        count++;
    }

    printf("Average age is %f\n", (float)sum / count);

    return 0;
}