1

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.

#include <iostream>
using namespace std;

int main()
{
int option;

do
{
    cout <<"Type random characters ( E.g : asdwefef ) " ;
    cin >> option;

    if (cin.good()) // If numeric
    {

    }
    else
    {
        cout << "Invalid input!" << endl;
        cin.clear();        // Clear buffer
        cin.ignore( INT_MAX, '\n' );
    }

}while (option != 0);

return 0;
}

How do I solve that? I have tried using the following, but the result is the same.

std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
tofi9
  • 5,775
  • 4
  • 29
  • 50
Thunder
  • 89
  • 1
  • 1
  • 7
  • 1
    I doubt you see the error message twice for this code. You're probably seeing `"Type random characters..."` twice and `"Invalid input!"` once. That's what I get when I reproduce your code. – David G May 10 '14 at 16:44
  • 2
    And the reason for that is because the stream doesn't parse the entire character sequence when extracting numeric input. It only takes what suffices, like the `"123"` part. The second part of the input `"abc"` is still left for the next extraction. Therefore, `cin.good()` is `true` on the first iteration. – David G May 10 '14 at 16:46
  • 1
    Also, you can only count on `option` being `0` after a failed extraction in C++11. Make sure your compiler supports that before acting on that assumption. Compiler versions – David G May 10 '14 at 16:52

1 Answers1

2

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
}
David G
  • 94,763
  • 41
  • 167
  • 253
  • Actually, `stoi` just extracts the initial valid part instead of checking the entire string. – chris May 10 '14 at 18:20
  • IIRC, `boost::lexical_cast` does check the whole string, but it has a couple other things people don't like. I also forgot this while writing my last comment, but `stoi` has the extra parameter that can give this information, so my last comment should really say that used minimally, it doesn't check the whole string, and you have to make sure it reaches the end yourself. – chris May 11 '14 at 03:10