Good Input Validation Loop Using Cin - C++

Good input validation loop using cin - C++

I'm not a huge fan of turning on exceptions for iostreams. I/O errors aren't exceptional enough, in that errors are often very likely. I prefer only to use exceptions for less frequent error conditions.

The code isn't bad, but skipping 80 characters is a bit arbitrary, and the error variable isn't necessary if you fiddle with the loop (and should be bool if you keep it). You can put the read from cin directly into an if, which is perhaps more of a Perl idiom.

Here's my take:

int taxableIncome;

for (;;) {
cout << "Please enter in your taxable income: ";
if (cin >> taxableIncome) {
break;
} else {
cout << "Please enter a valid integer" << endl;
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(), '\n');
}
}

Apart from only skipping 80 characters, these are only minor quibbles, and are more a matter of preferred style.

What is the best way to do input validation in C++ with cin?

Here is code you could use to make sure you also reject things like

42crap

Where non-number characters follow the number. If you read the whole line and then parse it and execute actions appropriately it will possibly require you to change the way your program works. If your program read your number from different places until now, you then have to put one central place that parses one line of input, and decides on the action. But maybe that's a good thing too - so you could increase the readability of the code that way by having things separated: Input - Processing - Output

Anyway, here is how you can reject the number-non-number of above. Read a line into a string, then parse it with a stringstream:

std::string getline() {
std::string str;
std::getline(std::cin, str);
return str;
}

int choice;
std::istringstream iss(getline());
iss >> choice >> std::ws;
if(iss.fail() || !iss.eof()) {
// handle failure
}

It eats all trailing whitespace. When it hits the end-of-file of the stringstream while reading the integer or trailing whitespace, then it sets the eof-bit, and we check that. If it failed to read any integer in the first place, then the fail or bad bit will have been set.

Earlier versions of this answer used std::cin directly - but std::ws won't work well together with std::cin connected to a terminal (it will block instead waiting for the user to input something), so we use a stringstream for reading the integer.


Answering some of your questions:

Question: 1. Using a try catch block. It didn't work. I think this is because an exception is not raised due to bad input.

Answer: Well, you can tell the stream to throw exceptions when you read something. You use the istream::exceptions function, which you tell for which kind of error you want to have an exception thrown:

iss.exceptions(ios_base::failbit);

I did never use it. If you do that on std::cin, you will have to remember to restore the flags for other readers that rely on it not throwing. Finding it way easier to just use the functions fail, bad to ask for the state of the stream.

Question: 2. I tried if(!cin){ //Do Something } which didn't work either. I haven't yet figured this one out.

Answer: That could come from the fact that you gave it something like "42crap". For the stream, that is completely valid input when doing an extraction into an integer.

Question: 3. Thirdly, I tried inputting a fixed length string and then parsing it. I would use atoi(). Is this standards compliant and portable? Should I write my own parsing function?

Answer: atoi is Standard Compliant. But it's not good when you want to check for errors. There is no error checking, done by it as opposed to other functions. If you have a string and want to check whether it contains a number, then do it like in the initial code above.

There are C-like functions that can read directly from a C-string. They exist to allow interaction with old, legacy code and writing fast performing code. One should avoid them in programs because they work rather low-level and require using raw naked pointers. By their very nature, they can't be enhanced to work with user defined types either. Specifically, this talks about the function "strtol" (string-to-long) which is basically atoi with error checking and capability to work with other bases (hex for example).

Question: 4. If I write a class that uses cin, but dynamically do this kind of error detection, perhaps by determining the type of the input variable at runtime, will it have too much overhead? Is it even possible?

Answer: Generally, you don't need to care too much about overhead here (if you mean runtime-overhead). But it depends specifically on where you use that class. That question will be very important if you are writing a high performance system that processes input and needs to have high throughout. But if you need to read input from a terminal or a file, you already see what this comes down to: Waiting for the user to input something takes really so long, you don't need to watch runtime costs at this point anymore on this scale.

If you mean code overhead - well it depends on how the code is implemented. You would need to scan your string that you read - whether it contains a number or not, whether some arbitrary string. Depending on what you want to scan (maybe you have a "date" input, or a "time" input format too. Look into boost.date_time for that), your code can become arbitrarily complex. For simple things like classifying between number or not, I think you can get away with small amount of code.

a while loop for input validation c++

I think the issue here is logic. If I'm not incorrect you want to ask for input until their input is one of A a B b C c?

Putting your while loop into words your saying:
while choice is A and a
or choice is B and b
or choice is C and c
Putting it that way it obviously can't work because how can choice be A and a?

What you want is to loop until choice is one of A a B b C c

Your return also shouldn't be in the while loop, I am assuming you want to return the choice they made after it is valid?

I also added the ! (this is "not", this inverts the result of a condtion - turns true into false, and false into true) operator before all of your conditions as without this you would be looping while choice is correct which means that it would continue only after an incorrect choice is entered.

In short, this code loops through asking for an input until the user's input is one of A a B b C c, after which it returns the input they gave.

while (!(choice == 'a' || choice == 'A' || choice == 'b' || choice == 'B' || choice == 'c' || choice == 'C')) {
std::cout << "We dont offer that package. Please try again:\n ";
std::cin >> choice;
}
return choice;

In english this is while choice isn't A or a or B or b or C or c then ask for input

Validating user input using cin

The problem is that you are testing the value of *(temperatures + i * days + j) even when the input has failed. Plus you are using ignore incorrectly (only ignoring one character instead of all outstanding characters). Plus you have overly complex code

Here's a better version

#include <limits> // for std::numeric_limits

cout << "temperature(" << i + 1 << ',' << j + 1 << ") = ";
int temp;
while (!(cin >> temp) || temp < -50 || temp > 50)
{
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(), '\n');
cout << "temperature(" << i + 1 << ',' << j + 1 << ") = ";
}
temperatures[i * days + j] = temp;

I used a new variable temp to simplify the code. I included cin >> temp in the while loop condition thereby only checking temp if the input has succeeded, and I used cin.ignore(numeric_limits<streamsize>::max(), '\n'); to ignore all characters remaining in the input.

Note this probably isn't perfect. If you entered say 10deg then the input would succeed (temp would equal 10) even though the input has non-digits in it. If you want to do input validation properly then the only real way is to read the input as a string, and test the string, before converting to an integer.

Correct input went into validation loop at least once after return from sub menu

Problem Solve by running cin.ignore() only when first time access

void passengerRecord()
{
int again = 0;
char option;
do
{
//menu syntax
//.
//.
if (again == 0) // only run when it's first time access during this call
cin.ignore();
while (option != '1' && option != '2' && option != '3' && option != '0')
{
//error input correction
}

//proceed to sub-menu

again++; // increment or again = 1; to indicate the menu had been accessed during this call
} while (option != '0');

}

While loop or function for input validation

You're correct in that you should put it into a function, encapsulation is always easier in the long run (even if you're never gonna look at this program again), and it's useful to keep good practice no matter the project!

If you're not intending on making any changes to the input and it's a case of valid input or invalid input, then you could always return a bool so you could do something like

if(inputValidator() == true)
{
//perform calculations
}
else
{
//Tell the user their input is incorrect and prompt for the input again
}

Trying to learn about input validation loops

The answer is, read first, check second, claim third. That is,

for (;;) { // the same as while(true)
cin >> rate;
if (rate is valid)
break;
cout << "Invalid rate\n";
}

And don’t mix cout and cerr without a reason. cout delays output, accumulating data in an internal buffer, while cerr prints immediately. (but cout flushes the buffer on cin use; it is also possible to call cout.flush [or cout << flush] explicitly).

Trying to use a while statement to validate user input C++

This is an annoying problem with cin (and istreams in general). cin is type safe so if you give it the wrong type it will fail. As you said a really large number or non-number input it gets stuck in an infinite loop. This is because those are incompatible with whatever type levelChoose may be. cin fails but the buffer is still filled with what you typed so cin keeps trying to read it. You end up in an infinite loop.

To fix this, you need to clear the fail bit and ignore all the characters in the buffer. The code below should do this (although I haven't tested it):

while(levelChoose > 10 || isalpha(levelChoose))
{
cout << "That is not a valid level" << endl;
cout << "Choose another level:";
if(!(cin >> levelChoose))
{
cin.clear();
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
}

Edit: numeric_limits<> is located in the limits include:

#include<limits>


Related Topics



Leave a reply



Submit