Avoid bad user input (string when what's asked is an integer) - c++

I have an infinite while loop, where the user is asked for a number.
My problem is very simple: If the input is a string, I want to re-ask user for input, outputting the message "Enter a valid choice: ".
I searched, and looks like I should check for cin.fail(), then call cin.clear() and cin.ignore().
Here's my code:
int main() {
int choice;
bool failed = false;
while (true) {
if (failed) cout << "Enter a valid choice: ";
else cout << "Enter a number: ";
cin >> choice;
if (cin.fail()) {
cin.clear();
cin.ignore();
failed = true;
}
}
return 0;
}
However, this doesn't really fix my problem. Of course, it isn't printing infinitely, but for each letter extra letter , it prints another "Enter a valid choice:"
Seems like I need to call cin.ignore() for each extra letter.
Any other way of doing this?

You have an infinite loop because you are not breaking the loop even when valid input is enter. Is that what you really want? If so, at the least, you are not resetting the failed flag in valid input.
More importantly, when invalid input is entered, you are not ignoring everything that was enteted, you are only ignoring 1 char at a time. That is why you see extra prompts.
Try this instead :
int main() {
int choice;
while (true) {
cout << "Enter a number: ";
while (!(cin >> choice)) {
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(), '\n');
cout << "Enter a valid choice: ";
}
}
return 0;
}

The reason it is printing so many times is because you are only clearing the state of cin, but aren't clearing the input buffer. You can do so in multiple ways:-
Use fflush(stdin) to clear the input buffer.This is the C method and can be done by including cstdio header.
Use the cin.ignore to ignore all characters in the current input stream. You can do this by replacing the line cin.ignore() which ignores a single character by this code cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n') which ignores the entire line. For this you need the limits header.
Finally you can do the same with a simple loop like while (cin.get() != '\n'){continue;} which ignores all characters till new line.
Also another approach to the same problem is to take the input in form of a string and use the strtol() or the isdigit() functions to check if the input is valid.
By the way the infinite loop is because you have not used the break statement to terminate the loop. So you can avoid this by adding
if(!failed)
break;
Also you need to change the state of Failed at the entry of each loop by adding
failed=false;
at the start of the loop body.

Related

How to validate integer input vs character input [duplicate]

In the following loop, if we type characters as the cin input instead of numbers which are expected, then it goes into infinite loop. Could anyone please explain to me why this occurs?
When we use cin, if the input is not a number, then are there ways to detect this to avoid abovementioned problems?
unsigned long ul_x1, ul_x2;
while (1)
{
cin >> ul_x1 >> ul_x2;
cout << "ux_x1 is " << ul_x1 << endl << "ul_x2 is " << ul_x2 << endl;
}
Well you always will have an infinite loop, but I know what you really want to know is why cin doesn't keep prompting for input on each loop iteration causing your infinite loop to freerun.
The reason is because cin fails in the situation you describe and won't read any more input into those variables. By giving cin bad input, cin gets in the fail state and stops prompting the command line for input causing the loop to free run.
For simple validation, you can try to use cin to validate your inputs by checking whether cin is in the fail state. When fail occurs clear the fail state and force the stream to throw away the bad input. This returns cin to normal operation so you can prompt for more input.
if (cin.fail())
{
cout << "ERROR -- You did not enter an integer";
// get rid of failure state
cin.clear();
// From Eric's answer (thanks Eric)
// discard 'bad' character(s)
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
For more sophisticated validation, you may wish to read into a string first and do more sophisticated checks on the string to make sure it matches what you expect.
Attention
Please pay attention to the following solution. It is not complete yet to clear the error in your case. You will still get an infinite loop!
if (cin.fail())
{
cout << "Please enter an integer";
cin.clear();
}
Complete Solution
The reason is you need clear the failed state of stream, as well as discard unprocessed characters. Otherwise, the bad character is still there and you still get infinite loops.
You can simply can std::cin.ignore() to achieve this. For example,
if (cin.fail())
{
cout << "Please enter an integer";
// clear error state
cin.clear();
// discard 'bad' character(s)
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
Another Solution
You can also use getline and stringstream to achieve. Here is a brief example.
string input;
while (1)
{
getline(cin, input);
stringstream(input) >> x;
cout << x << endl;
}
Perhaps, it's because,
cin is a streaming function. When a
non-numeric is entered instead of
numbers, it is ignored. And you are
prompted for re-entry.
Even if you did give numeric inputs,
you will be prompted for more inputs since you're on an infinite loop.
You can solve this problem like this:
1. Create a function to take in a string input.
2. return it as numeric after conversion. Use strtod() for conversion.
Hope this helps :)
Another alternative is operator! ,it is equivalent to member function fail()
//from Doug's answer
if ( !cin )
{
cout << "ERROR -- You did not enter an integer";
// get rid of failure state
cin.clear();
// From Eric's answer (thanks Eric)
// discard 'bad' character(s)
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}

Terminating a While loop when getting numbers [duplicate]

In the following loop, if we type characters as the cin input instead of numbers which are expected, then it goes into infinite loop. Could anyone please explain to me why this occurs?
When we use cin, if the input is not a number, then are there ways to detect this to avoid abovementioned problems?
unsigned long ul_x1, ul_x2;
while (1)
{
cin >> ul_x1 >> ul_x2;
cout << "ux_x1 is " << ul_x1 << endl << "ul_x2 is " << ul_x2 << endl;
}
Well you always will have an infinite loop, but I know what you really want to know is why cin doesn't keep prompting for input on each loop iteration causing your infinite loop to freerun.
The reason is because cin fails in the situation you describe and won't read any more input into those variables. By giving cin bad input, cin gets in the fail state and stops prompting the command line for input causing the loop to free run.
For simple validation, you can try to use cin to validate your inputs by checking whether cin is in the fail state. When fail occurs clear the fail state and force the stream to throw away the bad input. This returns cin to normal operation so you can prompt for more input.
if (cin.fail())
{
cout << "ERROR -- You did not enter an integer";
// get rid of failure state
cin.clear();
// From Eric's answer (thanks Eric)
// discard 'bad' character(s)
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
For more sophisticated validation, you may wish to read into a string first and do more sophisticated checks on the string to make sure it matches what you expect.
Attention
Please pay attention to the following solution. It is not complete yet to clear the error in your case. You will still get an infinite loop!
if (cin.fail())
{
cout << "Please enter an integer";
cin.clear();
}
Complete Solution
The reason is you need clear the failed state of stream, as well as discard unprocessed characters. Otherwise, the bad character is still there and you still get infinite loops.
You can simply can std::cin.ignore() to achieve this. For example,
if (cin.fail())
{
cout << "Please enter an integer";
// clear error state
cin.clear();
// discard 'bad' character(s)
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
Another Solution
You can also use getline and stringstream to achieve. Here is a brief example.
string input;
while (1)
{
getline(cin, input);
stringstream(input) >> x;
cout << x << endl;
}
Perhaps, it's because,
cin is a streaming function. When a
non-numeric is entered instead of
numbers, it is ignored. And you are
prompted for re-entry.
Even if you did give numeric inputs,
you will be prompted for more inputs since you're on an infinite loop.
You can solve this problem like this:
1. Create a function to take in a string input.
2. return it as numeric after conversion. Use strtod() for conversion.
Hope this helps :)
Another alternative is operator! ,it is equivalent to member function fail()
//from Doug's answer
if ( !cin )
{
cout << "ERROR -- You did not enter an integer";
// get rid of failure state
cin.clear();
// From Eric's answer (thanks Eric)
// discard 'bad' character(s)
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}

Character left on the input stream causing infinite loop [duplicate]

In the following loop, if we type characters as the cin input instead of numbers which are expected, then it goes into infinite loop. Could anyone please explain to me why this occurs?
When we use cin, if the input is not a number, then are there ways to detect this to avoid abovementioned problems?
unsigned long ul_x1, ul_x2;
while (1)
{
cin >> ul_x1 >> ul_x2;
cout << "ux_x1 is " << ul_x1 << endl << "ul_x2 is " << ul_x2 << endl;
}
Well you always will have an infinite loop, but I know what you really want to know is why cin doesn't keep prompting for input on each loop iteration causing your infinite loop to freerun.
The reason is because cin fails in the situation you describe and won't read any more input into those variables. By giving cin bad input, cin gets in the fail state and stops prompting the command line for input causing the loop to free run.
For simple validation, you can try to use cin to validate your inputs by checking whether cin is in the fail state. When fail occurs clear the fail state and force the stream to throw away the bad input. This returns cin to normal operation so you can prompt for more input.
if (cin.fail())
{
cout << "ERROR -- You did not enter an integer";
// get rid of failure state
cin.clear();
// From Eric's answer (thanks Eric)
// discard 'bad' character(s)
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
For more sophisticated validation, you may wish to read into a string first and do more sophisticated checks on the string to make sure it matches what you expect.
Attention
Please pay attention to the following solution. It is not complete yet to clear the error in your case. You will still get an infinite loop!
if (cin.fail())
{
cout << "Please enter an integer";
cin.clear();
}
Complete Solution
The reason is you need clear the failed state of stream, as well as discard unprocessed characters. Otherwise, the bad character is still there and you still get infinite loops.
You can simply can std::cin.ignore() to achieve this. For example,
if (cin.fail())
{
cout << "Please enter an integer";
// clear error state
cin.clear();
// discard 'bad' character(s)
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
Another Solution
You can also use getline and stringstream to achieve. Here is a brief example.
string input;
while (1)
{
getline(cin, input);
stringstream(input) >> x;
cout << x << endl;
}
Perhaps, it's because,
cin is a streaming function. When a
non-numeric is entered instead of
numbers, it is ignored. And you are
prompted for re-entry.
Even if you did give numeric inputs,
you will be prompted for more inputs since you're on an infinite loop.
You can solve this problem like this:
1. Create a function to take in a string input.
2. return it as numeric after conversion. Use strtod() for conversion.
Hope this helps :)
Another alternative is operator! ,it is equivalent to member function fail()
//from Doug's answer
if ( !cin )
{
cout << "ERROR -- You did not enter an integer";
// get rid of failure state
cin.clear();
// From Eric's answer (thanks Eric)
// discard 'bad' character(s)
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}

Catch ios::failure keeps looping

The code below should check every input once and display "Not a number" whenever the input is not a number.
int input;
while (1 == 1){
cout << "Enter a number: ";
try{
cin.exceptions(istream::failbit);
cin >> input;
}catch(ios::failure){
cout << "Not a number\n";
input = 0;
}
}
The problem is that when the catch is called (when it is not a number) it displays "Invalid number" endlessly like if the while() loop was executed several times but without asking for any new input.
while(true) or while(1) [or for(;;)] are customary ways to make a "forever loop".
You need to "clean up" the input that isn't acceptable within the cin stream.
The typical approach is to call cin.ignore(1000, '\n'); which will ignore all input until the next newline [up to 1000 characters - you can choose a bigger number, but usually a 1000 is "enough to get to a newline].
You will almost certainly also (thanks Potatoswatter) need to call cin.clear(); on the input, to remove the failed state, so that next input can succeed. [And cin.ignore() is further input, so it needs to go before that - just to be clear].
Though you failed to extract characters from the stream into an int, those characters remain in the stream so that you can attempt to extract them as something else, instead.
To skip them entirely, run std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); inside your catch block.
Then, whatever the user enters next will be the first thing in the stream. And perhaps that'll be a number, so your next attempt to extract into an int succeeds.
Well yeah. Your try-catch statement is inside the loop. So you try something, it fails and throws an exception, then you catch the exception, and you never exit or return from the loop so you do the same thing all over again.
But since your input wasn't processed the first time (throwing an exception instead), it's not going to be processed the second time, or the third time, or any time.
To advance, handle the exception by ignoring the input until the next space:
int input;
while (1 == 1){
cout << "Enter a number: ";
try{
cin.exceptions(istream::failbit);
cin >> input;
}catch(ios::failure){
cout << "Not a number\n";
input = 0;
//the line below ignores all characters in cin until the space (' ')
//(up to 256 characters are ignored, make this number as large as necessary
cin.ignore(256, ' ');
}
}
By the way, as a general rule: exceptions should be for something that is truly exceptional, particularly since there is overhead for handling the exception. There is debate about whether invalid user input is exceptional.
As an alternative, you can make a much more compact, equally correct loop without exceptions like the following:
int input;
while (true){ //outer while loop that repeats forever. Same as "while(1 == 1)"
cout << "Enter a number: ";
//The following loop just keeps repeating until a valid value is entered.
//The condition (cin >> input) is false if e.g. the value is a character,
//or it is too long to fit inside an int.
while(!(cin >> input)) {
cout << "Not a number" << endl;
input = 0;
}
}

Infinite loop with cin when typing string while a number is expected

In the following loop, if we type characters as the cin input instead of numbers which are expected, then it goes into infinite loop. Could anyone please explain to me why this occurs?
When we use cin, if the input is not a number, then are there ways to detect this to avoid abovementioned problems?
unsigned long ul_x1, ul_x2;
while (1)
{
cin >> ul_x1 >> ul_x2;
cout << "ux_x1 is " << ul_x1 << endl << "ul_x2 is " << ul_x2 << endl;
}
Well you always will have an infinite loop, but I know what you really want to know is why cin doesn't keep prompting for input on each loop iteration causing your infinite loop to freerun.
The reason is because cin fails in the situation you describe and won't read any more input into those variables. By giving cin bad input, cin gets in the fail state and stops prompting the command line for input causing the loop to free run.
For simple validation, you can try to use cin to validate your inputs by checking whether cin is in the fail state. When fail occurs clear the fail state and force the stream to throw away the bad input. This returns cin to normal operation so you can prompt for more input.
if (cin.fail())
{
cout << "ERROR -- You did not enter an integer";
// get rid of failure state
cin.clear();
// From Eric's answer (thanks Eric)
// discard 'bad' character(s)
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
For more sophisticated validation, you may wish to read into a string first and do more sophisticated checks on the string to make sure it matches what you expect.
Attention
Please pay attention to the following solution. It is not complete yet to clear the error in your case. You will still get an infinite loop!
if (cin.fail())
{
cout << "Please enter an integer";
cin.clear();
}
Complete Solution
The reason is you need clear the failed state of stream, as well as discard unprocessed characters. Otherwise, the bad character is still there and you still get infinite loops.
You can simply can std::cin.ignore() to achieve this. For example,
if (cin.fail())
{
cout << "Please enter an integer";
// clear error state
cin.clear();
// discard 'bad' character(s)
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
Another Solution
You can also use getline and stringstream to achieve. Here is a brief example.
string input;
while (1)
{
getline(cin, input);
stringstream(input) >> x;
cout << x << endl;
}
Perhaps, it's because,
cin is a streaming function. When a
non-numeric is entered instead of
numbers, it is ignored. And you are
prompted for re-entry.
Even if you did give numeric inputs,
you will be prompted for more inputs since you're on an infinite loop.
You can solve this problem like this:
1. Create a function to take in a string input.
2. return it as numeric after conversion. Use strtod() for conversion.
Hope this helps :)
Another alternative is operator! ,it is equivalent to member function fail()
//from Doug's answer
if ( !cin )
{
cout << "ERROR -- You did not enter an integer";
// get rid of failure state
cin.clear();
// From Eric's answer (thanks Eric)
// discard 'bad' character(s)
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}