关于C#:scanf EOF循环异常

While scanf EOF loop misbehaving

我是C语言的初学者,但遇到了无法解决的问题,也无法在此处找到其他线程的解决方案。

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

1
2
3
4
5
6
7
8
9
10
11
12
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:\
"
);
while (scanf("%d",&grade) != EOF)
{
    grades[gradesLength]=grade;
   gradesLength=gradesLength+1;
}

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

某种程度上来说,循环的行为不正确,似乎循环中可以使用某些输入,但是对于某些输入(实际上是大多数输入),scanf()并不会获得EOF,无论它是文件的实际结尾还是^ D终端中的命令。

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

我试图用== 1更改!= EOF及其所有相同。

用于输入

100 0 90 10 80 20 70 30 60 40 50

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

0 50 100

循环是无限的。

我正在使用macbook pro btw(如果有问题的话)。

有任何想法吗?


如果键入字母而不是数字,则scanf()将返回0(例如,"零成功转换的数字"),而不是EOF(例如,"没有数据可读取")。正确的测试是确保获得期望的值数-在这种情况下,为1:

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

如果您需要知道是到达EOF还是没有结果(但是读取其余行可能会解决问题),那么请从scanf()捕获返回值:

1
2
3
4
5
6
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…

而且,如果您确实愿意,可以编写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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 != '\
'
)
            putchar(c);
        putchar('\
'
);
        if (c == EOF)
            break;
    }
}

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

正如chux所指出的,在读取了可能是整数一部分的字符之后,需要将该字符放回输入流中。因此,如果我要分析该行的其余部分并重新开始扫描实际上可能是整数一部分的第一个数据,我会考虑使用类似以下内容的方法:

1
2
3
4
5
6
#include <ctype.h>

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

然后else子句可能是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
else
{
    printf("Discarding junk:");
    int c;
    while ((c = getchar()) != EOF && c != '\
'
&& !could_be_integer(c))
        putchar(c);
    putchar('\
'
);
    if (could_be_integer(c))
        ungetc(c, stdin);
    else if (c == EOF)
        break;
}

如您所见,这变得凌乱。有时(如Weather Vane在评论中指出的那样),使用fgets()读取一行然后在循环中使用sscanf()更容易(请参见如何在循环中使用sscanf()?)。警惕有关使用fflush(stdin)的建议;它并非在所有地方都会自动出错,但在正常情况下它无法在MacBook Pro上使用。

总体而言,简单地忽略输入的其余部分通常是更好的接口决策。


这个对我有用。

因此,我附上了您的摘录:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#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:\
"
);
    while (scanf("%d",&grade) != EOF)
    {
        grades[gradesLength]=grade;
        gradesLength=gradesLength+1;
    }

    if (errno)
        perror("grade");

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

并运行它:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ 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
$

也许您在寻找错误的位置。该错误可能在您的输出例程中?

就个人而言,如果必须这样做,并且对scanf何时返回的内容有一定的不确定性,并且无需重写它,那么这个小的更改可能更可靠:

1
2
3
4
5
int i, r;

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