Reputation: 187
I have a question about input validation in C++. Here is my code I am having trouble with:
#include <iostream>
using namespace std;
int main()
{
int in;
while(true)
{
if(cin >> in)
{
if(in < 0 || in > 2)
{
cout << "Invalid input. Number needs to be 0, 1, or 2.";
cin.clear();
while(cin.get() != '\n');
cin.ignore();
}
else
{
cout << "Output: " << in;
break;
}
}
else
{
cout << "Invalid input. Please enter a number.";
cin.clear();
while(cin.get() != '\n');
cin.ignore();
}
}
}
This code works fine unless two invalid entries are made in a row with the second input of the form '12hfhd'. Then it accepts this as input and I can't figure out why. I have searched on SO and have found a bunch of questions regarding input validation but can't seem to find any about their code accepting certain input.
Upvotes: 0
Views: 1923
Reputation:
The main problem is that, when requesting an int
from std::cin
using the >>
operator, a sequence of numeric chars from the beginning of the input will be converted. Examples:
2
will convert to 2
75$
will convert to 75
12asdfgh
will convert to 12
hello,world
will convert to 0
, because the first char is already not a numberThe best thing to do is to use some char
operations:
int getNumber() {
char input;
std::cin >> input;
if(input > '2' || input < '0') { // yes, chars act like ASCII numbers
// handle this problem
return -1; //error
} else {
return int(input - '0'); // input's code - '0''s code = number
}
}
Upvotes: 5
Reputation: 2780
If you look at the documentation of the >> extraction operator, for example here:
http://www.cplusplus.com/reference/istream/istream/operator%3E%3E/
you will notice the following quote:
(1) arithmetic types Extracts and parses characters sequentially from the stream to interpret them as the representation of a value of the proper type, which is stored as the value of val.
This essentially means that the program will try to treat all the data you pass as formatted in the format specified by the rvalue, in your case int. Simpler: in your case it will try to make ints of what you're passing into the stream and make the data as 'integerish' as it gets.
Upvotes: 0
Reputation: 17444
I'd use the following approach when dealing with user input:
string l;
if(!getline(cin, l))
/* handle error that no input could be read at all */
try
{
int const res = boost::lexical_cast<int>(l);
/* numerically validate input here */
return res;
}
catch(exception const&)
{
/* handle parsing error here */
}
In words, read a line and then parse and validate it using Boost's lexical_cast()
function template. Note that the first error, that getline()
fails, happens if someone reads input from a file (e.g. via shell redirect), but this can also be achieved with certain keypresses, depending on the shell. This state can't be recovered from, so prompting for a different answer will result in an endless loop.
Upvotes: 0