Why doesn't the scan return characters on failure?
I know that scanf
(and family) returns the number of arguments it read successfully. I also know that if that fails, the input is left untouched, so you can do things like this:
printf("%s", "Plese input a string or a float.\n");
float f;
char s[128];
if(scanf("%f", &f) == 1) {
//do something to respond to user answering with a float. (1)
} else if(scanf("%127s", s)) {
//do something to process the string. (2)
}
It turns out that scanf
doesn't work with the input. I expect scanf to try to read anything that matches <float here>
, and does nothing on failure, but instead happens scanf
, which will use the input, until some point it thinks becomes good point to stop.
For example: if I enter 1.2
, I end up in a branch (1)
and f = 1.2
as expected.
If I enter text
, the result is as expected, I return to (2)
and s = "text"
.
However, if input normal
, the result is what I end up with (2)
without additional user input and value s = "rmal"
. Why is it no
consumed outside of me.
I want to reproach that yes, I use fgets
instead scanf
where possible, thanks for your suggestion.
The question remains the same: "Why does scanf consume input even on error?"
source to share
According to the C99 spec (7.19.6.2, clauses 9 and 12):
an input element is defined as the longest sequence of input characters that does not exceed any given field width and that is or is a prefix of the corresponding input sequence. The first character, if any, after the input element remains unread.
a, e, f, g Matches an optionally signed floating point, infinity, or NaN number whose format is the same as expected for a sequence of strtod function objects.
7.20.1.3 paragraph 3 describes the strtod function:
The expected form of the subject sequence is an optional plus or minus sign, followed by one of the following:
- a non-empty sequence of decimal digits, optionally containing a decimal point character, followed by an optional exponent part as defined in 6.4.4.2;
- a 0x or 0X, then a non-empty sequence of hexadecimal digits, optionally containing a decimal point, followed by an optional binary exponent part as defined in 6.4.4.2;
- one of INF or INFINITY ignoring case
- one of NAN or NAN (n-char-sequence opt), ignoring the case in the NAN part,
para 6 adds
Unlike the "C" locale, additional language-related forms may be accepted.
This means that the standard conforming implementation of fscanf, if given normal
as an input to a directive %f
, MUST only consume n
(leave ormal
the input) and fail. So if your implementation also consumes o
, then either you are using a non-C language that accepts something starting with no
, or there seems to be a bug in the stdlib implementation.
source to share
The general answer is: scanf()
will read one character at a time, and this will continue as long as the characters read can lead to a successful conversion. Only one character is returned, which makes the conversion fail. There is stdio
FILE *
no way in streams to return multiple characters anyway.
In your specific example, it depends on the implementation of your C standard library and what it considers a valid representation float
. There are, for example, implementations that successfully parse strings such as nan
(not a number) or inf
(infinity). While I can't think of a valid representation float
starting at no
, your library seems to know it, or it's a bug trying to parse nan
and not return o
, which causes it to fail.
That being said, I cannot reproduce this right now in windows using msvcrt
.
In short, it's best to stay away from scanf()
.
source to share
I did a quick check and I didn't notice any problems:
Platform: Mac
Compiler: GCC
$ gcc --version Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1 Apple LLVM version 8.0.0 (clang-800.0.42.1) Target: x86_64-apple-darwin15.6.0 Thread model: posix InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin $ gcc -Wall main.c $ ./a.out Plese input a string or a float. 1.2 B1 1.200000 $ ./a.out Plese input a string or a float. 100 B1 100.000000 $ ./a.out Plese input a string or a float. test B2 test $ ./a.out Plese input a string or a float. normal B2 normal <<<<< No problem here
It looks like it is platform dependent.
source to share