2

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.

  • 1
    Just use `while (getline(cin, somestring))`. It will make your life easier. – Fantastic Mr Fox Jun 18 '14 at 03:21
  • More generally, read up on error handling in C++. Using try, catch, and throw will really help you out. – indiscrete Jun 18 '14 at 03:52
  • If you want to have really robust validation, use some regex. As of C++11, it's part of the STL. Would allow you to say something like "Password must be 12 characters, one capital and one special character" and check it in just a couple lines. Regex isn't nearly as hard as it may appear, and it is well worth learning. – jaredready Jun 18 '14 at 20:33

3 Answers3

1

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;
}
ekerstie
  • 11
  • 1
  • 3
  • The first solution only works if the second input is valid. If the second input is not valid either it stays in the stream `ss` because `ss.clear()` only resets the state flags, not the underlying string. This causes the extraction to `result` to always fail which ends in an infinite loop. A way to reset the stream completely would be for example `std::stringstream temp(input); ss.swap(temp)`. – Oli L Sep 27 '21 at 18:06
1

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?

Community
  • 1
  • 1
Alexander Oh
  • 24,223
  • 14
  • 73
  • 76
0
do {
        cin.sync()
        cin.clear();
        cout << "Enter radius: ";
        cin >> input;
} while(input < 0 && cin.fail());
  • I do not think this will work because if I input a `number < 0`, it will break out of the loop because `cin` will store the value because it is a negative number, and will not fail. For your condition to work, both sides of the `&&` operator will have to be `true` to continue the loop it would have to be `||`. – JustThatTypeOfGuy Jun 18 '14 at 18:13