JustThatTypeOfGuy
JustThatTypeOfGuy

Reputation: 21

How To Check If User's Input Is Valid? (C++)

When it comes to creating a program based on a set of instructions, I do pretty well in designing the pseudo-code, implementing the actual code. What I feel like I lack is checking for users' input (whether it's valid or invalid). As I practiced programming, I created my own way for checking for validating users' input. But the code is lengthy and I feel like it's insufficient (I'll explain why). I wanted to know if there is a better way to check for users' input. And how do other programmers implement their code.

This is how I validate users' input:

if(cin.fail()) {
        cout << "Invalid Input" << endl;
        cout << "Now Exiting..." << endl;
        return;
}
// I didn't know how to skip a line while in code
while(input < 0) {
        cout << "Invalid Input" << endl;
        cout << "Enter radius: " << endl;
        cin >> input;
        if(cin.fail()) {
             cout << "Error: Invalid Input" << endl;
             cout << "Now Exiting..." << endl;
             return;
        }
}

The reason why I exit out when cin fails to store the value into the variable separately (line 1 - 5, line 11 -15) is because if I add the cin.fail() to the while condition and attempt to input a letter, it begins a infinite loop. I did a little research and I saw you have to cin.sync(), then cin.clear(). But I still get the infinite loop.

Here is the code:

do {
        cin.sync()
        cin.clear();
        cout << "Enter radius: ";
        cin >> input;
} while(input < 0 || cin.fail());

If I'm doing something wrong, it would very helpful to see better ways to validate user's input.

Upvotes: 2

Views: 10133

Answers (3)

Alexander Oh
Alexander Oh

Reputation: 25621

The problem of input validation is an easy form of parsing.

There are language-classes (in the field of formal language theory) that express the complexity of your input. Those classes are called regular, context-free, and turing-complete.

You have to consider all your possible inputs, that your program might receive and decide whether your program should accept them or not. The language classes help you to decide what kind of input validation you need.

if the language is regular (as it is in your case) you can use regular expressions to validate the input.

A context-free language for example would be a math-formula. You cannot count the number of parentheses with a regular expression. Therefore it is impossible to check ((a+b) * (c+d)) has the right amount of parentheses with a regular expression.

Up to now these are hints on what you should be doing, when programming comes more naturally to you.

For the sake of simplicity well do a very constrained regular expression like parsing by hand. what you actually want to do in pseudo code:

do {
   std::cout << "Please enter radius: ";

   line = read_a_line_from(std::cin) // separated by '\n' the newline

   if (false == really_read_a_line(line)) {
      /* error handling for std::cin, dealing with i.e.: the fail bit */
      break; /* exit the loop */
   }
   if (line == "exit") { // give the user an explicit exit, to quit gracefully
      exit(SUCCESS); /* exit the program */
   }

   if (false == is_a_number(line)) {
      /* we read something that isn't a number */
      /* we should tell the user he should do as we asked */
      continue; /* jump back to the beginning of the loop */
   }

   unsigned num = convert_number(line);
   unsigned area = calculate_area(num); /* do something with your input */
} while (true);
exit(FAILURE);

The code here is not too specific on purpose that you see what you could be doing in places, still leaving out the actual implementation (for your exercise). Please note that a simple way of checking whether a line is actually a number is by converting. However not all things to parse should be checked for validity and processed at the same time.

See Also (especially the examples):

http://en.cppreference.com/w/cpp/string/basic_string/getline

http://en.cppreference.com/w/cpp/string/basic_string/stol

how to check if given c++ string or char* contains only digits?

Upvotes: 1

ekerstie
ekerstie

Reputation: 11

I would not recommend using std::cin, since it leaves all remaining user input after the first found instance of whitespace in the input buffer. This will create problems unless you remove the remaining characters using cin.ignore(). It is generally seen as better practice to use getline(), which will get all the characters up to the newline character. If you do choose to use std::cin, you will need to use cin.ignore() to remove the remaining characters, and cin.clear() to reset cin's fail bit so the while conditional will work properly the next time through the loop.

Below is how I would solve the problem. It uses getline() to get all the characters, and a stringstream to convert the string to an int. Notice you need to clear the stringstream's fail bit just like with cin to make sure the conditional works correctly when you do ss >> result in the while conditional.

std::cout << "Enter radius: ";
getline(std::cin, input);
std::stringstream ss(input);

while(!(ss >> result)) {
    std::cout << "Invalid Input" << std::endl;
    std::cout << "Enter radius: ";
    getline(std::cin, input);
    ss.clear();
    ss << input;
}

Below I'll also include some code to solve the problem using std:cin. I still recommend using getline() though. Note: std::numeric_limits::max() is used to specify how many characters to remove from the input buffer. Using this instead of your own arbitrary number is a better practice, since you can't know for certain how many characters the user will enter. cin.ignore() will remove all the characters up to the given number or until it reaches an instance of the character provided as its second parameter, which in this case is newline ('\n').

std::cout << "Enter radius: ";
std::cin >> result;

while(std::cin.fail()) {
    std::cout << "Invalid Input" << std::endl;
    std::cout << "Enter radius: ";
    std::cin.clear();
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    std::cin >> result;
}

Upvotes: 1

user3128725
user3128725

Reputation: 1

do {
        cin.sync()
        cin.clear();
        cout << "Enter radius: ";
        cin >> input;
} while(input < 0 && cin.fail());

Upvotes: 0

Related Questions