This is my code which display error when user\'s input is not numeric. However, when user inputs alphanumberic ( eg: 123abc ), it repeats the error message twice.
Input streams parse characters one by one. For numeric extraction, the stream will continually read characters until it finds one that is non-numeric. This doesn't set std::ios_base::failbit
if it successfully wrote characters to its operand already, and if there was no attempt to read the non-numeric character. Thus, std::cin.good()
will return true
for the first iteration.
Generally, checking good()
is not the preferred means of assessing stream validity. Streams have an internal boolean operator that does this for you. All you have to do is enclose the actual input operation in a boolean expression:
if (std::cin >> option) {
// successful input
}
else {
// unsuccessful
}
Now, to check if the entire input is numeric, it would be best to read into a string and do the parsing manually, as streams cannot do this on their own (by default). Alternatively, to make the stream do this itself, you can create a customized std::num_get<char>
facet that sets the error mask if it can determine that the input was not entirely numeric. This facet will be installed into the stream's locale; you can uninstall it at any time by changing to the original:
class num_get : public std::num_get<char>
{
public:
iter_type do_get( iter_type it, iter_type end, std::ios_base& str,
std::ios_base::iostate& err, long& v) const
{
auto& ctype = std::use_facet<std::ctype<char>>(str.getloc());
it = std::num_get<char>::do_get(it, end, str, err, v);
if (it != end && !(err & std::ios_base::failbit)
&& ctype.is(ctype.alpha, *it))
err |= std::ios_base::failbit;
return it;
}
};
Install it into the locale and imbue()
the locale into the stream to get the desired behavior:
std::locale original_locale(std::cin.getloc());
std::cin.imbue(std::locale(original_locale, new num_get));
if (std::cin >> option) {
// input was entirely numeric
}
else {
// input was not entirely numeric
}