I'm trying to filter out invalid user inputs in a small C++ program using the following chunk of code:
int selection = -1;
while (!(selection >= 1 && selection <=4))
{
cin >> selection;
if (!(selection >= 1 && selection <=4))
{
cout << "invalid selection!" << endl;
cout << "selection: ";
}
}
It seems to work fine when I enter any numerical value that is either inside or outside the range i want to filter. However strange things happen when I enter invalid values such as values larger than the maximum storable int or characters. The code loops through and skipping the "cin" command.
How do I fix this?
Thanks
You need to detect unconvertible input using fail() and then ignore the rest of the bad data and reset cin error flags using clear() before reading in a new input attempt.
int selection = -1;
while (!(selection >= 1 && selection <=4))
{
cin >> selection;
if (cin.fail() || !(selection >= 1 && selection <=4))
{
cout << "invalid selection!" << endl;
cout << "selection: ";
cin.clear();
cin.ignore(std::numeric_limits<int>::max(), '\n');
}
}
Here are two suggestions for fixing your issue:
Add Error Handling to cin
Read as String And Parse
The common solution is Read As String And Parse, but I'm presenting both for you to choose.
Add Error Handling to cin
When the stream extraction function receives a character that is not suited for numerics, it sets a fail bit. You need to check the state of the stream (cin) for failure. If you want to continue, you need to clear the error state.
The state can be checked by using the fail method: cin.fail(). To clear the state use: cin.clear().
See C++ Online Reference -- istream
Read As String And Parse
An alternative is read the input as a string, then parse the string for your data. The string container has some useful methods for parsing.
Use getline to read in a string variable from cin.
Again, you will have to write code to check for errors and process them.
You need to check condition of cin, and if it is any error states, you should clear the error flag and ignore():
int main()
{
int selection = -1;
while (!(selection >= 1 && selection <=4))
{
if (cin >> selection)
{
if (!(selection >= 1 && selection <=4))
{
cout << "invalid selection!" << endl;
cout << "selection: ";
}
}
else
{
cin.clear();
cin.ignore();
}
}
cout << selection << " selected\n";
return 0;
}
Another way is to read everything as string and let the stringstream do error check:
int main()
{
int selection = -1;
while (!(selection >= 1 && selection <=4))
{
string line;
getline(cin, line);
stringstream sstr(line);
if (sstr >> selection)
{
if (!(selection >= 1 && selection <=4))
{
cout << "invalid selection!" << endl;
cout << "selection: ";
}
}
else
{
cout << "invalid input!" << endl;
cout << "selection: ";
}
}
cout << selection << " selected\n";
return 0;
}
Note: You may need to put a cin.ignore(); right after getline() call - depending on if you are reading all unformatted input or not.
You should read cin into a std::string and start your validation from there. Check that the value is numeric, for starters.
When you enter letters for example it puts cin into a bad state and leaves those characters. You need to ignore them (cin.ignore(1024, '\n')) so it knows to move on to the next line of input.
Related
So I figure I'll put this here since I had to traverse a lot of docs and forums to find the definitive answer. I was trying to get input from the user and check if the input was an integer using isdigit() in an if statement. If the if statement failed the program would output an error message. Although, when a nondigit character was entered the program would loop through the error message endlessly. Here's that code:
int guess = -1;
while (game.getCurQuestion() <= 4) {
std::cout << "Guess: " << game.getCurQuestion() + 1 << std::endl;
std::cin >> guess;
if(isdigit(guess))
{
game.guess(guess);
else
{
std::cout << "Error\n"; //this would be looped endlessly
}
}
std::cout << "You got " << game.getCorrect() << " correct" << std::endl;
return 0;
}
NOTE: Solved, only posted to include my solution. Feel free to correct if I stated anything incorrectly.
The posted way will fail sometimes and will cast the doubles to integers if any doubles are input.
Use something like the following
int getIntInput() {
try {
std::string input;
std::cout << "\nPlease Enter a valid Integer:\t";
std::cin >> input;
size_t takenChars;
int num = std::stoi(input, &takenChars);
if (takenChars == input.size()) return num;
} catch (...) {}
return getIntInput();
}
Problem: The program kept hold of the non-integer value stored in the cin buffer. This leads to the program never leaving the error message.
Solution:
Use std::cin.fail() to check if the input matches the variable data type. I.E. int was the expected input but the user entered a char. In this case std::cin.fail() would be true.
In the case of std::cin.fail(), use std::cin.clear() and std::cin.ignore(std::numeric_limits<int>::max(), 'n') std::cin.clear() will clear the error flag. The std::cin.ignore(std::numeric_limits<int>::max(), 'n') will ignore any other input that is not an integer and will skip to the new line. Effectively progressing the program.
The solution implemented in my code looks like this:
int guess = -1;
while (game.getCurQuestion() <= 4) {
std::cout << "Guess: " << game.getCurQuestion() + 1 << std::endl;
std::cin >> guess;
if (std::cin.fail())
{
std::cout << "Please enter a valid number\n";
std::cin.clear();
std::cin.ignore(std::numeric_limits<int>::max(), '\n');
}
game.guess(guess);
}
Hope this helps and that it saves some people the tedious research because of never learning std::cin error handling! Note: I'm aware my implementation skips the current move, call it punishment ;)
This code works fine if I enter something that isn't a number in, e.g. F: it will print the error message. However, if I enter e.g. 2F2 or , it will take the 2 and pass the check, continue in my code and on the next cin >> statement it will put the F in, and then it loops back and puts the 2 in.
How do I make it so it only accepts a single number e.g. 2 and not e.g. 2F2 or 2.2?
int bet = 0;
// User input for bet
cout << " Place your bet: ";
cin >> bet;
cout <<
// Check if the bet is a number
if (!cin.good())
{
cin.clear();
cin.ignore();
cout << endl << "Please enter a valid number" << endl;
return;
}
bool Checknum(std::string line) {
bool isnum = true;
int decimalpoint = 0;
for (unsigned int i = 0; i < line.length(); ++i) {
if (isdigit(line[i]) == false) {
if (line[i] == '.') {
++decimalpoint; // Checks if the input has a decimal point that is causing the error.
}
else {
isnum = false;
break;
}
}
}
if (decimalpoint > 1) // If it has more than one decimal point.
isnum = false;
return isnum;
}
If you take a string from the user, this should work. You can convert the string to an integer or a float(stoi or stof, respectively). It may not be the best solution there is, but this is what I have. Excuse the indentation.
Do getline to read one whole line of input from cin.
Create a stringstream to parse the string you got.
In this parser, read the number; if it fails - error
Read whitespace; if it doesn't arrive to the end of string - error
#include <sstream>
...
int bet = 0;
std::cout << " Place your bet: ";
while (true)
{
std::string temp_str;
std::getline(cin, temp_str);
std::stringstream parser(temp_str);
if (parser >> bet && (parser >> std::ws).eof())
break; // success
cout << endl << "Please enter a valid number" << endl;
}
This code keeps printing the error message until it receives valid input. Not sure this is exactly what you want, but it's pretty customary UI.
Here >> ws means "read all the whitespace". And eof ("end of file") means "end of the input string".
I have a program which has the ability to reject user input if a char is entered instead of an int, and this works almost perfectly - anything entered that isn't a number is being rejected.
However, all of these cins need to accept any value between a minimum and a maximum, but I can't get it to work. The code below shows my efforts so far, but there's a slight bug. If a char is entered, followed by an int that is out of range, and another char is entered (I like to test rigorously - I mean, who knows what could happen if an actual end user came across the problem) the program throws the final value of mortgageTerm out as 0.
Could anyone tell me where I'm going wrong and give me any pointers to help me fix it? Thanks in advance to anyone who's able to help me solve my problem!
int mortgageTerm;
string line;
cout << "Mortgage term (1 - 40 years) : ";
while (!(cin >> mortgageTerm))
{
cout << "That's not a valid choice! Try again : ";
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(), '\n');
}
while (getline(cin, line))
{
stringstream linestream;
if (!linestream >> mortgageTerm)
{
cout << "Input was not a number! Try again : ";
cin >> mortgageTerm;
continue;
}
if ((mortgageTerm <= 0 || mortgageTerm > 40))
{
cout << "Input out of range. Try again : ";
cin >> mortgageTerm;
continue;
}
char errorTest;
if (linestream >> errorTest)
{
cout << "Invalid input. Try again : ";
cin >> mortgageTerm;
continue;
}
break;
}
cout << mortgageTerm;
You're almost there. Your first issue is your first while loop is not needed at all. Then we just need to tweak the second loop to make sure that all the input read was used in the value you get. We can also simplify it by using a single error statement, Making those changes gives you
int mortgageTerm;
string line;
cout << "Mortgage term (1 - 40 years) : ";
while (getline(cin, line)) // consume all input given
{
stringstream linestream(line); // you have to construct the stream from the string here
linestream >> mortgageTerm; // try and read the data
if (!linestream.eof() || mortgageTerm <= 0 || mortgageTerm > 40)
{
// either there is input left in linestream or the value is not in range
cout << "Invalid input. Try again : ";
}
}
Just check for the minimum and maximum in the same condition where you check if it was able to be converted into an int, using ||, in a condition the expressions are checked left to right in order, so the first did its work already when you evaluate the second and mortageTerm will have the value.
Edited to address comments.
int mortgageTerm;
cout << "Mortgage term (1 - 40 years) : ";
while (!(cin >> mortgageTerm) ||
mortageTerm < 1 ||
mortgageTerm > 40 )
{
cout << "That's not a valid choice! Try again : ";
cin.clear();
cin.ignore(std::numeric_limits<streamsize>::max(), '\n');
}
// If you are concerned about extra input after the number and want to clear the input stream
// cin.ignore(std::numeric_limits<streamsize>::max(), '\n');
I have a while loop here that only takes in 1 and 2 as the number, if i insert and number that is not these my else statement will keep asking for the correct one, which works correctly. But if i insert a letter my else statement loops forever. How can i fix this?
#include <iostream>
using namespace std;
int main()
{
int myChoice;
cin >> myChoice;
while ( myChoice >= 2 || myChoice <= 1)
{
if (myChoice == 1)
{
cout <<"food1";
break;
}
else if (myChoice == 2)
{
cout <<"food2";
break;
}
else
{
cout << " " << endl;
cout << "Please select the proper choices" << endl;
cout << "Try again: ";
cin >> myChoice;
}
}
return 0;
}
If you enter a non-number, then cin >> myChoice fails. That means that it leaves the input intact in the input buffer and when you get there again it tries to parse it and fails, and so on... You must clear the error state and ignore the non-digits. The simplest way is something like this:
cout << "Try again: ";
cin.clear(); // clear error state
cin.ignore(std::numeric_limits<streamsize>::max(), '\n'); // ignore till the end of line
cin >> myChoice;
The problem here is that the cin >> operator expects to receive an int input and receives a char input.
The istream module, of which cin is an instance, is using buffered I/O. This means that the user input is first stored in a buffer, and then read from that buffer when the user program accesses the >> operator. Ordinarily, if the >> operator succeeds in reading and parsing the user input, the read data is extracted from the buffer and the next invocation of the >> operator would continue where the last call left off. In you case, however, the >> operator attempts to parse the user input as a number and fails since it contains illegal chars which are not digits. The >> operator doesn't extract the read data from the buffer in this case and this same data is being referred to over and over again in the following calls to the >> operator.
You should empty the buffer on failure, the way ybungalobill suggested, for instance.
Your while condition is always true, then you use break to exit the loop. You could simplify things a bit like this:
#include <iostream>
using namespace std;
int main()
{
int myChoice;
cin >> myChoice;
while( myChoice != 1 || myChoice != 2 ) {
cout << endl;
cout << "Please select the proper choices" << endl;
cout << "Try again: ";
cin.clear();
cin.ignore(std::numeric_limits<streamsize>::max(), '\n');
cin >> myChoice;
}
// At this point myChoice is 1 or 2
if (myChoice == 1)
cout << "food1";
else if (myChoice == 2)
cout << "food2";
}
I need to know how to make my cin statement not appear to 'remove' itself if you input the wrong type. The code is here:
int mathOperator()
{
using namespace std;
int Input;
do
{
cout << "Choose: ";
el();
cout << "1) Addition";
el();
cout << "2) Subtraction";
el();
cout << "3) Multiplication";
el();
cout << "4) Division";
el();
el();
cin >> Input;
}
while (Input != 1 && Input != 2 && Input!=3 && Input!=4);
return Input;
}
Execute, enter, for example, a character, and it loops nonstop acting as though the cin statement isn't there.
You must check that input succeeded and handle when it doesn't:
int mathOperator() {
using namespace std;
int Input;
do {
cout << "Choose: ";
el();
cout << "1) Addition";
el();
cout << "2) Subtraction";
el();
cout << "3) Multiplication";
el();
cout << "4) Division";
el();
el();
while (!(cin >> Input)) { // failed to extract
if (cin.eof()) { // testing eof() *after* failure detected
throw std::runtime_error("unexpected EOF on stdin");
}
cin.clear(); // clear stream state
cin.ignore(INT_MAX, '\n'); // ignore rest of line
cout << "Input error. Try again!\n";
}
} while (Input != 1 && Input != 2 && Input!=3 && Input!=4);
return Input;
}
If you don't check that extraction succeeded, then cin is left in a failed state (cin.fail()). Once in a failed state, later extractions will immediately return instead of trying to read from the stream, effectively making them no-ops – leading to your infinite loop.
Unless you're quite certain about the input being in the proper format, you rarely want to use operator>> directly from the input stream.
It's usually easier to read a line with std::getline, put that into a std::istringstream, and read from there. If that fails, you print/log an error message, throw away the remainder of the line and (possibly) go on to the next line.
After reading in a bad value, cin is in a "failed" state. You have to reset this.
You must both clear the error flag and empty the buffer. thus:
cin.clear();
cin.ignore(std::numeric_limits<streamsize>::max(), '\n');
The second call "flushes" the input buffer of any data that might be there, to get you ready for the next "cin" call.
If you find yourself writing these 2 lines "all over your code" you could write a simple inline function to replace it.
inline void reset( std::istream & is )
{
is.clear();
is.ignore( std::numeric_limits<std::streamsize>::max(), '\n' );
}
Although I have made this function take any istream, most of the time it would only be used for cin where a user is entering and enters something invalid. If it's an invalid file or stringstream input, there is no way to fix it and you would do best to just throw an exception.
don't read int, read char so cin will pass any invalid character
char Input;
do
{
// same code
}
while (Input != '1' && Input != '2' && Input != '3' && Input!='4');
return Input;
[EDIT]
If you want convert char to int you can use this piece of code
int i = (Input - 48);
I agree that a char is just as handy, since you can always cast to int, to answer your question as to why this is happening, when a cin input is exected as an int but a char is entered, the input is kept in the input stream for the duration of the loop, which is why it seems to "disappear."
For more information: see the post from Narue at http://www.daniweb.com/forums/thread11505.html