Home > Software engineering >  The best way to capture user input int with error handling loop
The best way to capture user input int with error handling loop

Time:10-27

In my case, I have to make sure the user input is either 1 or 2, or 3.

Here's my code:

#include <iostream>

using namespace std;

void invalid_choice_prompt() {
    string msg = "\nInvalid Command! Please try again.";
    cout << msg << endl;
}

int ask_user_rps_check_input(int user_choice) {
    if (user_choice == 1 || user_choice == 2 || user_choice == 3) return 1;
    return 0;
}

int ask_user_rps() {
    // ask user's choice of Rock or Paper or Scissors
    while (1) {
        string msg =
            "\nPlease enter your choice:\nRock - 1\nPaper - 2\nScissors - 3";
        cout << msg << endl;

        int user_choice;
        cin >> user_choice;

        if (ask_user_rps_check_input(user_choice)) {
            return user_choice;
        }
        invalid_choice_prompt();
    }
}

int main() {
    ask_user_rps();

    return 0;
}

The code is capable to handle the situation when the input is an integer, but when the input are characters or strings, the program will be trapped in the infinite loop.

Is there any elegant way to do this? I've found some methods about using cin.ignore to ignore the specified length of io buffer, but I don't think this method is flexible enough. I am looking for a more flexible solution.

CodePudding user response:

One of the simplest solution would be to check the cin stream failure something like below:

int ask_user_rps() {
// ask user's choice of Rock or Paper or Scissors
while (1) {
    string msg =
        "\nPlease enter your choice:\nRock - 1\nPaper - 2\nScissors - 3";
    cout << msg << endl;

    int user_choice;
    cin >> user_choice;
    
    if(cin.fail()) {
        invalid_choice_prompt();
        std::cin.clear();
        std::cin.ignore(256,'\n');
        continue;
    }

    if (ask_user_rps_check_input(user_choice)) {
        return user_choice;
    }
    invalid_choice_prompt();
}
}

CodePudding user response:

Reading from a stream using operator >> takes as many characters from the stream as the target type accepts; the rest will remain in the stream for subsequent reads. If the input has a format error (e.g. a leading alphabetical characters when an integer is expected), then an error-flag is set, too. This error-flag can be checked with cin.fail(). It remains set until it gets explicitly cleared. So if your code is...

int user_choice;
cin >> user_choice;

and if you then enter something that is not a number, e.g. asdf, then user_choice has an undefined value, an error-flag cin.fail() is (and reamins) set. So any subsequent read will fail, too.

To overcome this, you have to do three things:

First, check the error-flag. You can do this either through calling cin.fail() after a read attempt of through checking the return value of the expression (cin >> user_choice), which is the same as calling cin.fail().

Second, in case of an error, you need to clear the error-flag using cin.clear(). Otherwise, any attempt to read in anything afterwards will fail.

Third, if you want to continue with reading integral values, you need to take the invalid characters from the stream. Otherwise, you will read in asdf into a variable of type integer again and again, and it will fail again and again. You can use cin.ignore(numeric_limits<streamsize>::max(),'\n'); to take all characters until EOF or an end-of-line from the input buffer. The complete code for reading an integral value with error-handling could look as follows:

int readNumber() {

    int result;
    
    while (!(cin >> result)) {
        cin.clear();
        cin.ignore(numeric_limits<streamsize>::max(),'\n');
        cout << "Input is not a number." << std::endl;
    }

    return result;
}

CodePudding user response:

I think an option would be to collect the user input to a string and then move it to stringstream using getline kind of like this:

std::string input;
std::getline(std::cin, input);
//Now check if the input is correct. if it is, then:
std::stringstream stream;
stream << input;
int num;
stream >> num; 

I'm not sure if this is a good method but it works.

CodePudding user response:

Take input as char

string user_choice;
cin >> user_choice;

check input is valid or not if(user_choice=='1')

  • Related