I am confused about scanf\'s behaviour in the following program. scanf appears to input once, and then not input again, until a stream of characters is printed.
Belo
For scanf, you need to check its return value to see if the conversion on the input worked. scanf will return the number of elements successfully scanned. If the conversion did not work, it will leave the input alone, and you can try to scan it differently, or just report an error. For example:
if (scanf("%d", &i) != 1) {
char buf[512];
fgets(buf, sizeof(buf), stdin);
printf("error in line %d: got %s", j, buf);
return 0;
}
In your program, since the input is left alone, your loop repeats trying to read the same input.
In C++, you check for failure using the fail method, but the input stream failure state is sticky. So it won't let you scan further without clearing the error state.
std::cin >> i;
if (std::cin.fail()) {
std::string buf;
std::cin.clear();
std::getline(cin, buf);
std::cout
<< "error in line " << j
<< ": got " << buf
<< std::endl;
return 0;
}
In your program, since you never clear the failure state, the loop repeats using cin in a failure state, so it just reports failure without doing anything.
In both cases, you might find it easier or more reliable to work with the input if you would read in the input line first, and then attempt to parse the input line. In pseudocode:
while read_a_line succeeds
parse_a_line
In C, the catch to reading a line is that if it is longer than your buffer, you will need to check for that and concatenate multiple fgets call results together to form the line. And, to parse a line, you can use sscanf, which is similar to scanf but works on strings.
if (sscanf(buf, "%d", &i) != 1) {
printf("error in line %d: got %s", j, buf);
return 0;
}
In C++, for quick low level parsing of formatted input, I also prefer sscanf. But, if you want to use the stream approach, you can convert the string buffer into a istringstream to scan the input.
std::getline(cin, buf);
if (std::cin.fail()) {
break;
}
std::istringstream buf_in(buf);
buf_in >> i;
if (buf_in.fail()) {
std::cout << "error in line " << j
<< ": got " << buf
<< std::endl;
return 0;
}