而scanf EOF循环行为不当

时间:2014-12-30 23:17:38

标签: c while-loop scanf infinite-loop eof

我是C的初学者,我遇到了无法解决的问题,而且无法在其他线程上找到解决方案。

我尝试使用以下代码从键盘输入/ txt文件中读取整数:

int grades[MAX_GRADES_LENGTH]={0}, histogram[HISTOGRAM_SIZE]={0};
int maxGradesHistogramBucket=0, median=0, gradesLength=0;
double avg=0.0;
int grade=0;

printf("Enter grades:\n");
while (scanf("%d",&grade) != EOF)
{
    grades[gradesLength]=grade;
   gradesLength=gradesLength+1;
}

我应该设置这些"等级"在grades []数组中,并沿循环计算数组的长度。

不知何故循环行为不端,似乎循环中的某些输入正常,但对于某些输入(实际上大部分是输入),scanf()无法获得EOF,无论它是否为实际文件结束,或终端中的^ D命令。

我听说scanf()不是读取数据最可靠的方法,但不幸的是我无法使用其他任何东西,因为我们还没有在课堂上学到任何其他方法,所以我们可以只在我们的作业中使用scanf()。

我已尝试使用!= EOF更改== 1,并且完全相同。

输入

  

100 0 90 10 80 20 70 30 60 40 50

例如它工作正常。 但是对于输入:

  

0 50 100

循环是无限的。

我正在使用macbook pro btw(如果重要的话)。

有什么想法吗?

2 个答案:

答案 0 :(得分:2)

如果您输入一个字母而不是一个数字,scanf()将返回0(如,"零成功转换的数字")而不是EOF(如,"有没有数据可供阅读")。正确的测试是确保您获得预期数量的值 - 在本例中为1:

while (scanf("%d", &grade) == 1)

如果你需要知道你是否得到EOF或没有得到结果(但是阅读其余部分可能会清除问题),然后从scanf()获取返回值:

int rc;
while ((rc = scanf("%d", &grade)) == 1)
{
}
if (rc != EOF)
    …read the rest of the line, or at least the next character, before resuming the loop…

而且,如果你真的想,你可以写:

int rc;
while ((rc = scanf("%d", &grade)) != EOF)
{
    if (rc == 1)
        grades[gradesLength++] = grade;
    else
    {
        printf("Discarding junk: ");
        int c;
        while ((c = getchar()) != EOF && c != '\n')
            putchar(c);
        putchar('\n');
        if (c == EOF)
            break;
    }
}

else子句中的代码可以合理地放入函数中。它还可能将消息报告给标准错误而不是标准输出。让用户知道你反对的内容是有礼貌的。您可以使用&& !isdigit(c) && c != '+' && c != '-'中的isdigit()使用不同的测试<ctypes.h>在换行前停止。但是,用户没有机会重新编辑他们放在字母后面的内容,因此您可能会误解他们的输入。最好只丢掉输入线的其余部分,让它们重新开始。


作为chux noted,在读取可能是整数一部分的字符后,需要将该字符放回输入流中。因此,如果我要分析剩余的行并重新开始扫描第一个可能实际上是整数的数据的数据,我会考虑使用类似的东西:

#include <ctype.h>

static inline int could_be_integer(int c)
{
    return isdigit(c) || c == '+' || c == '-');
}

然后else子句可能是:

else
{
    printf("Discarding junk: ");
    int c;
    while ((c = getchar()) != EOF && c != '\n' && !could_be_integer(c))
        putchar(c);
    putchar('\n');
    if (could_be_integer(c))
        ungetc(c, stdin);
    else if (c == EOF)
        break;
}
如你所见,这会变得混乱。有时(Weather Vane中注明为comment),使用fgets()读取一行更容易,然后在循环中使用sscanf()(请参阅How to use sscanf() in a loop?) 。警惕关于Using fflush(stdin)的建议;它无处不在,但在正常情况下它不会在MacBook Pro上运行。

总的来说,简单地忽略输入线的其余部分通常是一个更好的界面决策。

答案 1 :(得分:1)

它对我有用。

我随后附上了你的摘录:

#include <stdio.h>
#include <errno.h>

#define MAX_GRADES_LENGTH 20
#define HISTOGRAM_SIZE 20

main()
{

    int grades[MAX_GRADES_LENGTH]={0}, histogram[HISTOGRAM_SIZE]={0};
    int maxGradesHistogramBucket=0, median=0, gradesLength=0;
    double avg=0.0;
    int grade=0;
    int i;

    printf("Enter grades:\n");
    while (scanf("%d",&grade) != EOF)
    {
        grades[gradesLength]=grade;
        gradesLength=gradesLength+1;
    }

    if (errno)
        perror("grade");

    for (i = 0; i < gradesLength; ++i) {
        printf("%d\n", grades[i]);
    }
}

并运行它:

$ a.out
Enter grades:
100 0 90 10 80 20 70 30 60 40 50
100
0
90
10
80
20
70
30
60
40
50
$ a.out
Enter grades:
0 50 100
0
50
100
$

也许你看错了地方。也许这个bug出现在你的输出程序中?

就个人而言,如果这样做,考虑到scanf在什么时候返回时的一些不确定性,并且没有重写它,那么这个小改变可能更可靠:

int i, r;

printf("Enter grades:\n");
while ((r = scanf("%d",&grade)) > 0)