You should never, under any circumstances, use scanf
or its relatives for anything, for three reasons:
- Many format strings, including for instance all the simple uses of
%s
, are just as dangerous as gets
.
- It is almost impossible to recover from malformed input, because
scanf
does not tell you how far in characters into the input it got when it hit something unexpected.
- Numeric overflow triggers undefined behavior: yes, that means
scanf
is allowed to crash the entire program if a numeric field in the input has too many digits.
Prior to C++11, the C++ specification defined istream
formatted input of numbers in terms of scanf
, which means that last objection is very likely to apply to them as well! (In C++11 the specification is changed to use strto*
instead and to do something predictable if that detects overflow.)
What you should do instead is: read entire lines of input into std::string
objects with getline
, hand-code logic to split them up into fields (I don't remember off the top of my head what the C++-string equivalent of strsep
is, but I'm sure it exists) and then convert numeric strings to machine numbers with the strtol
/strtod
family of functions.
I cannot emphasize this enough: THE ONLY 100% RELIABLE WAY TO CONVERT STRINGS TO NUMBERS IN C OR C++, unless you are lucky enough to have a C++ runtime that is already C++11-conformant in this regard, IS WITH THE strto*
FUNCTIONS, and you must use them correctly:
errno = 0;
result = strtoX(s, &ends, 10); // omit 10 for floats
if (s == ends || *ends || errno)
parse_error();
(The OpenBSD manpages, linked above, explain why you have to do this fairly convoluted thing.)
(If you're clever, you can use ends
and some manual logic to skip that colon, instead of strsep
.)
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…