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?"

+3


source to share


3 answers


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.

+3


source


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()

.

+3


source


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.

+2


source







All Articles