My C++ professor is adamant that when checking for input failure, one must use separate while() loops for each individual input. He indicated that the following method of checking more than one input gathered in a single cin statement would not work:
while (!good){
cout << "Enter the length and the width of the rectangle: ";
cin >> length >> width;
if(!cin){
cout << "Bad input, try again" << endl;
cin.clear();
cin.ignore(200, '\n');}
else {
good = true;}}
His proposed method:
bool good = false;
while (!good){
cout << "Enter the length rectangle: ";
cin >> length;
if(!cin){
cout << "Bad input, try again" << endl;
cin.clear();
cin.ignore(200, '\n');}
else {
good = true;}}
while (good){
cout << "Enter the width rectangle: ";
cin >> width;
if(!cin){
cout << "Bad input, try again" << endl;
cin.clear();
cin.ignore(200, '\n');}
else {
good = false;}}
My method above seems to work just fine. If non-numerical characters are inputted for either of the inputs, the loop will prompt for new input and clear the fail-state.
Is it simply considered bad form to check for input failure as I have done? I'm hoping to learn more clearly why my method is erroneous. If my method is faulty, is there a way to check for input failure without using separate loops for every single user-input? I mainly ask because I'm writing a program that involves getting many inputs from a file, and individually checking each variable seems overly tedious. I'm thinking there must be a better way.
Both are "correct" in that they recover from failed inputs and give the user another chance.
Both are only useful for interactive input. You mention reading from a file -- there's really no recovery possible. Ignoring a mangled field will simply cause the next field to be consumed and interpreted differently from what its location in the file indicates. Best course of action when reading from a file is to output as good an explanation as possible (exactly where in the file the error occurred, e.g. line and columns numbers, what kind of data was expected, and what about the data encountered was invalid).
In interactive usage, clearing the buffer and prompting again is useful.. Your version has the disadvantage that after entering a length correctly, if the user fat-fingers the width, they can't just re-enter the wrong data, but have to type the length a second time as well.
Writing the loop a second time is incredibly pointless, however. Instead you should write a helper function, something like:
template<typename T>
T prompt_for_value( const char* const prompt )
{
T result;
while (true) {
std::cout << prompt << std::flush;
if (std::cin >> result) return result;
std::cout << "Bad input, try again" << std::endl;
}
}
double width = prompt_for_value<double>("Enter the width in meters: ");
double height = prompt_for_value<double>("Enter the height: ");
Notice that the code is shorter overall and avoids the clunky use of the good variable which inverted its meaning halfway through the original code. Also, the call site now is very clean and focuses completely on the important information -- the prompt and the data type of the input.
Thanks the C++11 lambda support, it's now also very easy to add parameter validation to the helper function:
T prompt_for_value( const char* const prompt, std::function<bool(T)> validation = {} )
{
T result;
while (true) {
std::cout << prompt << std::flush;
if (std::cin >> result) {
if (validation && !validation(result)) {
std::cout << "Input out of range, try again" << std::endl;
continue;
}
return result;
}
std::cout << "Bad input, try again" << std::endl;
}
}
double width = prompt_for_value<double>("Enter the width in meters: ",
[](int w) { return w >= 0; });
double height = prompt_for_value<double>("Enter the height: ",
[&width](int h) { return (h >= 0) && (h <= 10000.0 / width); }));
What you have indicated as your professor's proposed method is very bad form as given due to violation of the don't-repeat-yourself (DRY) principle in the code. If you want to do basically the same thing multiple times, you should either use a loop or a function to avoid writing duplicate logic.
Your method involves all-or-nothing checking and therefore may require the user to repeat themselves. The usability of this should be decided on a case-by-case basis.
Both methods fail to detect extraneous characters at the end of the input line. Such input might reasonably be interpreted as erroneous -- regardless of whether or not it happens to work with the subsequent input request.
However, as you have demonstrated, the all-or-nothing approach does in fact basically work. Given that, here's an alternative all-or-nothing implementation based on the automatic exception-based checking that the iostream library is capable of.
cin.exceptions( ~std::ios::goodbit );
int length=0, width=0;
for ( bool ok=false; !ok; )
{
try
{
cout << "Enter the length and the width of the rectangle: ";
cin >> std::skipws >> length >> width;
std::string s;
std::getline( cin, s );
s.erase( std::remove_if( s.begin(), s.end(), ::isspace ), s.end() );
if ( !s.empty() ) throw std::runtime_error("too much input");
ok = true;
}
catch( std::ios::failure const & e )
{
cout<<"\n"<<"bad input ["<<e.what()<<"], try again...\n";
cin.clear();
cin.ignore( std::numeric_limits<std::streamsize>::max(), '\n' );
}
catch( std::runtime_error const & e )
{
cout<<"\n"<<e.what()<<", try again...\n";
}
}
cout<<"\n"<<"length="<<length<<", width="<<width<<"\n";
Output:
Enter the length and the width of the rectangle: x
bad input [basic_ios::clear], try again...
Enter the length and the width of the rectangle: 1 x
bad input [basic_ios::clear], try again...
Enter the length and the width of the rectangle: 1 2 x
too much input, try again...
Enter the length and the width of the rectangle: 1 2 3
too much input, try again...
Enter the length and the width of the rectangle: 4 5
length=4, width=5
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 ;)
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.
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;
}
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?
do {
cin.sync()
cin.clear();
cout << "Enter radius: ";
cin >> input;
} while(input < 0 && cin.fail());
I have a game where the user needs to enter x and y coordinates within a certain range, one at a time. Right now my input code is as follows:
do {
printf("\n");
cout << "X: ";
cin >> x;
cout << "Y: ";
cin >> y;
} while (cin.fail());
I'm new to c++ but reading documentation lead me to believe this was an acceptable method for verifying user input. It works perfectly when the input is valid, however when the input is of a different type (for instance entering "a") it infinitely loops with "X: Y: ". What do I need to do differently to have it wait for user input as if the lines were being read for the first time?
You should check every single input operation:
std::cout << "X: ";
if (!(std::cin >> x)) { /* error */ }
std::cout << "Y: ";
if (!(std::cin >> y)) { /* error */ }
It's up to you how you want to handle the error. You could return early, break from a loop, throw an exception... it all depends.
Note that you could loop until you get something parseable, but that's dangerous if the user closes the input stream. Better to read line by line and parse:
std::cout << "Please enter an integer X: ";
int x;
bool success = false;
for (std::string line; std::getline(std::cin, line); )
{
std::istringstream iss(line);
if (iss >> x >> std::ws && iss.get() == EOF) // #1
{
success = true;
break;
}
std::cout << "Sorry, please try again: ";
}
if (!success)
{
std::cerr << "Unexpected end of input stream!\n";
std::exit(1);
}
This way, if the user presses [Ctrl]-D in the middle of the session, the program shuts down immediately and doesn't try to read more values from the closed input stream.
The condition on the line marked #1 tests both for a successful parsing of an integer as well as for having reached the end of the line (gobbling intermediate whitespace).
The biggest problem with trying to parse per variable is that when a user makes a mistake, their whole input becomes suspect.
Consider if I skip invalid input and enter: "3e4 40". I meant "34 40", but skipping the e might make "3 4" and leave "40" for later or might leave "4 40".
Consider using getline to read a whole line in and parsing it such as with istringstream just as you are now -- any error becomes an error without leaving the inputstream in a weird state.
I am writing a text-based Scrabble implementation for a college project.
The specification states that the user's position input must be read from single line, like this:
Coordinates of the word's first letter and orientation (<A – P> <1 – 15> <H ou V>): G 5 H
G 5 H is the user's input for that particular example. The order, as shown, must be char int char.
What is the best way to read the user's input?
cin >> row >> column >> orientation will cause crashes if the user screws up.
A getline and a subsequent string parser are a valid solution, but represent a bit of work.
Is there another, better, way to do this, that I am missing?
Thanks for your time!
getline and parsing doesn't necessarily have to add much work. Since you already know how to read (correct) data from a stream, just read a line with getline, then create an istringstream from the line and read from there.
The one thing I'd add would be that it might very well make sense to create a class to hold the data for a particular move, and overload operator>> for that class to read data for a move. A rough sketch would be something like this:
class move {
char row;
int column;
char orientation;
public:
friend std::istream &operator>>(std::istream &is, move &m);
};
std::istream &operator>>(std::istream &is, move &m) {
std::string temp;
std::getline(is, temp);
std::istringstream buffer(temp);
// Now we can parse data from buffer just like we could from another stream.
return is;
}
At least for the moment, I haven't included any error handling code. Depending on how picky you want to be, this can get a little tricky (e.g., if input from the stringstream fails, setting the fail bit in the original input stream).
Sorry, but getline and parsing the string are your best bet. However, you can make your system a little bit more reusable by creating a class to represent the input options and then overloading operator>> so that it uses getline and parses the string. That way, you don't have to repeat any of your parsing code.
I got something like this:
#include <iostream>
#include <limits>
#include <string>
using namespace std;
template<class T> T getValue(const string& name)
{
T ret;
while(!(cin >> ret))
{
// normally here you'd go into an infinite loop, but since you're going to ignore the rest of the line, you can ensure that you won't go into an infinite loop and you can re-ask the user to input the correct data
cout << "Invalid input for " << name << " please try again" << endl;
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(), '\n');
}
return ret;
}
int main(void)
{
bool valid = false;
char row, orientation;
int column;
do {
cout << "Enter row, column, and orientation (<A-P> <1-15> <H to V>): " << endl;
row = getValue<char>("row");
column = getValue<int>("column");
orientation = getValue<char>("orientation");
if(row<'A' || row>'P')
cout << "Invalid row please enter A-P" << endl;
else if(column<1 || column>15)
cout << "Invalid column please enter 1-15" << endl;
else if(orientation<'H' || orientation>'V')
cout << "Invalid orientation please enter H-V" << endl;
else
valid = true;
} while(!valid);
cout << "Row: " << row << endl
<< "Column: " << column << endl
<< "Orientation: " << orientation << endl;
return 0;
}
Of course, if you enter something invalid like:
A B C
It would produce some potentially confusing problems. The first A would be successfully copied in row char variable. However since B is not numerical, it would ignore the remaining buffer, so you lose B and C. You get an error message that you entered an invalid input for column, but once you successfully enter a valid number, you would still have to enter a orientation again. So the user isn't clear on that point based on this application. You can make such modifications easily such that if you enter an invalid input, it would reask you to enter the whole thing.
Another suggestion is to input from the console, using one item at a time and apply an error checking:
char row;
bool is_valid = false;
while (!is_valid)
{
while (!(cin >> row))
{
cout << "Error reading row, please enter data again.\n";
}
row = toupper(row);
static const std::string valid_rows("ABCDEFGHIJKLMNO");
is_valid = valid_rows.find(row) != std::string::npos;
if (!is_valid)
{
cout << 'Row ' << row << ' is not a valid row letter, please re-enter.\n";
}
}
By reading one variable at a time, rather than all three at once, you can give the user earlier warning about error detection.
I can't seem to figure out why this falls into a loop after getting non-int input. I've tried cin.flush(), which doesn't seem to exist, cin.clear(), which seems like it should work, even cin.sync() after reading someone else post about it working, but didn't seem to make much sense. Also tried cin.bad().
Thank you very much for any help
Please enter the first number: f
Sorry, I don't think that's a number?
Please enter the first number: Sorry,
I don't think that's a number?
Please enter the first number: Sorry,
I don't think that's a number?
Please enter the first number: Sorry,
I don't think that's a number?
Please enter the first number: Sorry,
I don't think that's a number?Sorry,
you d on't get any more tries. Press
any key to continue . . .
#include <iostream>
using namespace std;
int main(){
int entry;
int attempts = 1;
int result;
while(attempts <= 5) {
try {
cout << "\n\nPlease enter the first number: ";
cin >> entry;
if (cin.fail())
throw "Sorry, I don't think that's a number?";
if (entry < 0)
throw "Sorry, no negative numbers. Try something else? ";
cout << "\nNow the second number: ";
cin >> entry;
cin.clear();
cin.get();
}
catch (char* error) {
cout << error;
attempts++;
}
}
if (attempts > 5)
cout << "Sorry, you don\'t get any more tries.\n";
system("pause");
return 0;
}
You should think carefully what you want to do if user gives invalid input in this case. Usually in these cases the best solution is to read one line from the input and throw it away.
Try putting cin.clear() and std::cin.ignore(std::numeric_limits<streamsize>::max(),'\n'); in your catch clause. cin.clear() clears the failure state in cin, and cin.ignore() throws away rest of the line waiting in the input buffer.
(And yes, you probably should rethink your use of exceptions).
The most straight-forward (but not usually the easiest nor the fastest) way of dealing with validation of line-based input is to always read it line at a time. This way no extra whitespace (such as linefeed characters) is left unread in the buffer in any case, and discarding erroneous input is also quite automatic.
// Infinite loop for retrying until successful
while (true) {
// Ask the user something
std::cout << prompt;
// Read the answer (one full line)
std::string line;
if (!std::getline(std::cin, line))
throw std::runtime_error("End of input while expecting a value");
// Put the line read into iss for further parsing
std::istringstream iss(line);
int val;
// Read val from iss and verify that reading was successful and
// that all input was consumed
if (iss >> val && iss.get() == EOF) return val;
std::cout << "Invalid input, try again!\n";
}
It is fun to make a BASIC style input function out of this:
template <typename Val> void input(std::string const& prompt, Val& val) {
// (the above code goes here, slightly adjusted)
}
int main() {
int w;
double l;
input("Enter weight in kg: ", w);
input("Enter length in m: ", l);
std::cout << "BMI: " << w / (l * l) << std::endl;
}
Notes for the pedantics who were going to downvote me:
function input should be specialized for std::string
exceptions thrown by the input function should be caught in main
My Problem was to block char input to a cin >> number
This error caused an 'infinite' loop showing my prompt cout << prompt
with no way of exit but kill the process ...
The following shows what worked for me!
========================================
double fi_trap_d() // function to return a valid range double catching errors
{
double fi_game_sec;
//-------------------------------------------
do
{
fi_game_sec = -1;
cout << fi_game_sec_c;
//------------------------------
cin.ignore(); // (1)
//------------------------------
try
{ cin >> fi_game_sec; cin.clear(); } // (2)
catch (...) //out_of_range)
{
fi_game_sec = -1;
cout << " Dis am an error!\n";
// just loop back as we asked for a number
}
} while (fi_game_sec < 1);
//-------------------------------------------
return fi_game_sec;
}
========================================
Despite trying the " Dis am an error! " has NEVER shown up.
The key was (1) & (2) !
Exceptions should be used to handle exceptional, unexpected situations. Incorrect input from a user is neither unexpected nor exceptional -- it's more or less the norm. Personally, I tend to just ignore most bad input completely (when it can't be prevented). When (and only when) they enter something unusable repeatedly is it even worth pointing it out to them. As such, I'd tend to write the code something like:
char ch;
int attempts = 0;
std::cout << "Please enter the first number: ";
do {
cin >> ch;
attempts++;
if (attempts > 5)
std::cerr << "The only allowable inputs are '0' through '9'\n";
} while (cin.good() && !isdigit(ch));
int first_number = ch - '0';
This reads the input as a character, so it's always removed from the input stream. Then it attempts to validate the input, and if it fails, attempts to read again. Of course, you might want/need to get a little more elaborate, such as reading an entire line, attempting to convert it to a number, and reading another line if that fails.
Why are you doing this with exceptions? You're not going to kill the program on input, so you shouldn't throw an exception.
Just print out your error message and attempt to read in again.
It looks like you would be better off with iostream's native exceptions. Enable with
cin.exceptions( ios::failbit );
try {
…
} catch( ios_base::failure & ) {
cin.clear();
…
}
Never, ever throw an an object not derived from std::exception, and especially not a native type like char*.