views:

94

answers:

5

I'm trying to filter out invalid user inputs in a small C++ program using the following chunk of code:

    int selection = -1;
    while (!(selection >= 1 && selection <=4))
    {   
        cin >> selection;

        if (!(selection >= 1 && selection <=4))
        {
            cout << "invalid selection!" << endl;
            cout << "selection: ";
        }
    }

It seems to work fine when I enter any numerical value that is either inside or outside the range i want to filter. However strange things happen when I enter invalid values such as values larger than the maximum storable int or characters. The code loops through and skipping the "cin" command.

How do I fix this?

Thanks

A: 

When you enter letters for example it puts cin into a bad state and leaves those characters. You need to ignore them (cin.ignore(1024, '\n')) so it knows to move on to the next line of input.

Mark B
How exactly would i use this ignore? What does cin.ignore(1024) mean? Specifically the 1024 part.
Faken
This tells to ignore the next 1024 characters, which is wrong, because the next 1024 characters will be ignored.
BatchyX
Maybe you wanted to use cin.ignore(1024, '\n') which tells to ignore all characters until a newline character is found or 1024 characters has been ignored
BatchyX
Ugh, indeed, only ignore up to a newline.
Mark B
A: 

You should read cin into a std::string and start your validation from there. Check that the value is numeric, for starters.

egrunin
+3  A: 

Here are two suggestions for fixing your issue:

  1. Add Error Handling to cin
  2. Read as String And Parse

The common solution is Read As String And Parse, but I'm presenting both for you to choose.

Add Error Handling to cin

When the stream extraction function receives a character that is not suited for numerics, it sets a fail bit. You need to check the state of the stream (cin) for failure. If you want to continue, you need to clear the error state.

The state can be checked by using the fail method: cin.fail(). To clear the state use: cin.clear().

See C++ Online Reference -- istream

Read As String And Parse

An alternative is read the input as a string, then parse the string for your data. The string container has some useful methods for parsing.

Use getline to read in a string variable from cin.

Again, you will have to write code to check for errors and process them.

Thomas Matthews
+1  A: 

You need to detect unconvertible input using fail() and then ignore the rest of the bad data and reset cin error flags using clear() before reading in a new input attempt.

int selection = -1;
while (!(selection >= 1 && selection <=4))
{   
    cin >> selection;

    if (cin.fail() || !(selection >= 1 && selection <=4))
    {
        cout << "invalid selection!" << endl;
        cout << "selection: ";

        cin.clear();

        cin.ignore(std::numeric_limits<int>::max(), '\n');
    }
}
Steve Townsend
+1  A: 

You need to check condition of cin, and if it is any error states, you should clear the error flag and ignore():

int main()
{
    int selection = -1;
    while (!(selection >= 1 && selection <=4))
    {
        if (cin >> selection)
        {
            if (!(selection >= 1 && selection <=4))
            {
                cout << "invalid selection!" << endl;
                cout << "selection: ";
            }
        }
        else
        {
            cin.clear();
            cin.ignore();
        }
    }

    cout << selection << " selected\n";

    return 0;
}

Another way is to read everything as string and let the stringstream do error check:

int main()
{
    int selection = -1;
    while (!(selection >= 1 && selection <=4))
    {
        string line;
        getline(cin, line);

        stringstream sstr(line);
        if (sstr >> selection)
        {
            if (!(selection >= 1 && selection <=4))
            {
                cout << "invalid selection!" << endl;
                cout << "selection: ";
            }
        }
        else
        {
            cout << "invalid input!" << endl;
            cout << "selection: ";
        }
    }

    cout << selection << " selected\n";

    return 0;
}

Note: You may need to put a cin.ignore(); right after getline() call - depending on if you are reading all unformatted input or not.

Donotalo