I'm a beginner in C, and I've got problem I can't figure out, and wasn't able to find a solution on other threads here.
I'm trying to read integers from a keyboard input/ txt file with the following code:
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;
}
I'm supposed to set these "grades" in the grades[] array and count the length of the array along the loop.
Somehow the loop is misbehaving, it seems that some inputs are ok with the loop, but for some inputs(most of them actually) the scanf() doesn't get the EOF, whether it's an actual end of file, or the ^D command in the terminal.
I've heard the scanf() is not the most reliable method to read data, but unfortunately I can't use anything else since we haven't learned any other method in class so we can only use scanf() on our homework.
I've tried to change the!= EOF
with == 1
and its all the same.
for the input
100 0 90 10 80 20 70 30 60 40 50
for example it works fine. but for the input:
0 50 100
the loop is infinite.
I'm using a macbook pro btw (if it matters).
Any ideas?
If you type a letter instead of a number,
scanf()
will return 0 (as in, "zero successfully converted numbers") and not EOF (as in, "there was no data left to read"). The correct test is to ensure that you got the expected number of values — in this case, 1:If you need to know whether you got to EOF or got no result (but reading the rest of the line might clear the problem), then capture the return value from
scanf()
:And, if you really want to, you could then write:
The code in the
else
clause could plausibly be put into a function. It might also report the messages to standard error rather than standard output. It is courteous to let the user know what it was that you objected to. You could stop before newline with a different test (&& !isdigit(c) && c != '+' && c != '-'
, usingisdigit()
from<ctypes.h>
). However, the user doesn't have a chance to re-edit the stuff they put after the letters, so you may be going to misinterpret their input. It is probably better just to throw away the rest of the line of input and let them start over again.As chux noted, after reading a character that could be part of an integer, that character needs to be put back into the input stream. Therefore, if I were going to analyze the rest of the line and restart scanning at the first data that could actually be part of an integer, I'd consider using something like:
and then the else clause might be:
This gets messy, as you can see. Sometimes (as Weather Vane noted in a comment), it is easier to read a line with
fgets()
and then usesscanf()
in a loop (see How to usesscanf()
in a loop?). Be wary of suggestions about Usingfflush(stdin)
; it isn't automatically wrong everywhere, but it won't work on a MacBook Pro under normal circumstances.On the whole, simply ignoring the rest of the line of input is usually a better interface decision.
It works for me.
I enclosed your snippet thus:
and ran it:
Perhaps you are looking in the wrong place. Maybe the bug is in your output routine?
Personally, if had to do this, given some ambiquity over what
scanf
returns when, and without rewriting it, then this small change is probably more reliable: