infinite loop when std::cin reaches eof despite clearing it - c++

I'm experiencing an infinite loop in a simple and tiny C++ code where I try to read user input and repeat when input fails until it correctly fills an integral type:
std::size_t size{};
while (std::cout << "Enter size: " && !(std::cin >> size))
{
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cout << std::endl;
}
But despite clearing std::cin and ignoring buffer contents, the loop gets running infinitely without prompting for user input.
What am I missing?
What should be done to make std::cin block io and read user input in each iteration after it fails?

Related

Is cin.fail with my while loop causing my output to infinitely loop? [duplicate]

I'm writing a program where I get an integer input from the user with cin>>iUserSel;. If the user puts in a letter, the program goes to an infinite loop. I tried to prevent that with the code below, but the program goes to an infinite loop and prints out "Wrong! Enter a #!". How can I fix my program?
cin>>iUserSel;
while (iValid == 1)
{
if (cin.fail())
{
cin.ignore();
cout<<"Wrong! Enter a #!"<<endl;
cin>>iUserSel;
}//closes if
else
iValid = 0;
}//closes while
I found some information on this at Correct way to use cin.fail() and C++ cin.fail() question
, but I didn't understand how to use them to fix my issue.
When cin fails, you need to clear the error flag. Otherwise subsequent input operations will be a non op.
To clear the error flags, you need to call cin.clear().
Your code would then become:
cin >> iUserSel;
while (iValid == 1)
{
if (cin.fail())
{
cin.clear(); // clears error flags
cin.ignore();
cout << "Wrong! Enter a #!" << endl;
cin >> iUserSel;
}//closes if
else
iValid = 0;
}//closes while
I would also suggest you change
cin.ignore();
to
cin.ignore(numeric_limits<streamsize>::max(), '\n');
In case the user enters more than one letter.
The problem you are having is that you don't clear the failbit from the stream. This is done with the clear function.
On a somewhat related note, you don't really need to use the fail function at all, instead rely of the fact that the input operator function returns the stream, and that streams can be used in boolean conditions, then you could do something like the following (untested) code:
while (!(std::cin >> iUserSel))
{
// Clear errors (like the failbit flag)
std::cin.clear();
// Throw away the rest of the line
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cout << "Wrong input, please enter a number: ";
}
Here's what I would recommend:
// Read the data and check whether read was successful.
// If read was successful, break out of the loop.
// Otherwise, enter the loop.
while ( !(cin >> iUserSel) )
{
// If we have reached EOF, break of the loop or exit.
if ( cin.eof() )
{
// exit(0); ????
break;
}
// Clear the error state of the stream.
cin.clear();
// Ignore rest of the line.
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
// Ask more fresh input.
cout << "Wrong! Enter a #!" << endl;
}

Using consecutive while(cin >> input) successfully [duplicate]

This question already has answers here:
std::cin loops even if I call ignore() and clear()
(2 answers)
Closed 5 years ago.
My first while loop executes, until I enter a non-number to terminate it. Then, instead of while(cin >> cel) executing, it is skipped, leading the program to terminate/finish. I have tried everything including clearing the "cin bit" as described in another similiar question with no success. What am I doing wrong?
int main() {
double fah = 0;
cout << "Enter a fahrenheit value:\n";
while (cin >> fah) { // executes until a non-number input is entered
cout << fah << "F == " << fah_to_cel(fah) << "C\n";
}
// tried cin.clear(); here
// tried cin.clear(ios_base::eofbit); here
double cel = 0;
cout << "Enter a celcius value:\n";
while(cin >> cel) { // executes until a non-number input is entered
cout << cel << "C == " << cel_to_fah(cel) << "F\n";
}
return 0;
}
You were correct to call cin.clear(). That resets the error flags of cin, which you need to do before you can perform any more input operations. But you need to do one more thing. When input fails, whatever characters cin was trying to read remain in the input buffer. So when you try to collect input again (after clearing the error), it will fail again. So you need to remove the data that was left in the buffer. You can do that like this:
std::streamsize amount_to_ignore = std::numeric_limits<std::streamsize>::max();
std::cin.ignore(amount_to_ignore, '\n');
This tells cin to discard all characters in its buffer until it finds a newline character (which should be in there from when you last pressed the enter key).
This is, in my opinion, a very clunky and error prone way to do user input. I would suggest that you exclusively use std::getline when reading from cin, which should never fail (except in the unlikely event of a memory allocation failure). And then parsing the resulting string manually, which gives you a lot more control over the form of the input.

C++ if(!cin) causes loop

I tried to use if(!cin) to validate if the user input really is an integer. However my programm then just goes into an infinite loop never asking vor new input
do{
cin >> temp->data;
if(!cin){
cout << "Please enter a Number!" << '\n';
correct=false;
}
}while(correct==false);
Would be great if someone could help me :)
When std::cin fails to read the input, the appropriate error flags are set. Therefore you want to reset the flags using std::cin.clear() so that the next input operation will work correctly and then skip everything until the new line using std::cin.ignore(..) in order to avoid similarly formatted input.
while (!(std::cin >> temp->data))
{
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cout << "\nPlease enter a number!" << std::endl;
}
std::numeric_limits<std::streamsize>::max() returns the max amount of characters a stream can hold so that the whole line is guaranteed to be ignored.
If you want to do that kind of check, read the data from cin to a string and convert the string to a number:
string str;
do{
cin >> str;
if(!cin){
cout << "Please enter a Number!" << '\n';
correct=false;
}
else{
istringstream stream(str);
stream >> temp->data;
if(!stream){
cout << "Please enter a Number!" << '\n';
correct=false;
}
}
}while(correct==false);
Use cin.fail() to check whether the user entered correct input. cin.fail() returns true if the last cin command failed, and false otherwise. Moreover, your loop is likely to be infinite, so you must also state an else where you will set the check flag correct to true. Thus, to invalidate the loop's condition and exit the loop in the case user entered correct input (see code below):
do{
cin >> temp->data;
if(cin.fail()){
cin.clear();
cin.ignore(10000, '\n');
cout << "Please enter a Number!" << '\n';
correct=false;
} else {
correct=true;
}
}while(correct==false);
Your 'correct' variable actually doesn't do anything the way you are using it. It's not possible to exit the loop without correct being true; so you could do away with it, and just use a loop-exiting command when you have read the number.
Also, none of the answers posted so far handle the input being closed. They would go into an infinite loop in that scenario.
// A loop; we will break out when we successfully read a number.
while ( 1 )
{
// Prompt for a number and read it
cout << "Please enter a Number!" << endl;
cin >> temp->data;
// Exit loop if we successfully read
if ( cin )
break;
// Check to see if we failed due to the input being closed
if ( cin.eof() )
{
cerr << "End of input reached.\n";
return 0; // depends what your function returns of course
}
// reset the error condition that was caused by trying to read an integer and failing
cin.clear();
// discard anything they previously typed
cin.ignore(numeric_limits<streamsize>::max(), '\n');
}
Moving on from this, a good design would be to actually have this code be an entire function in itself. Then you can call the function whenever you need to get a number safely, without needing to duplicate code. The function declaration might be:
void input_number(int &the_number, std::istream &in, std::string prompt);
which would output the_number, and it would handle end-of-file either by throwing an exception, or by relying on the caller to check for !cin, or even by returning a bool; whatever fits in best with your error handling overall.
For one, if you put a "!" before a condition in an if statement. That's supposed to be a "not" operator.

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;
}
}

cin condition checking error

I am a beginner programmer learning c++. I am having a nagging issue with the cin command.
In the program section below, if I enter a wrong type at the 1st cin command, the program will not execute any of the following cin commands at all, but will execute the rest of the program.
//start
#include <iostream>
using namespace std;
int main()
{
int x=0;
cout << endl << "Enter an integer" << endl;
//enter integer here. If wrong type is entered, goes to else
if (cin >> x){
cout << "The value is " << x << endl;
}
else {
cout << "You made a mistake" << endl; //executes
cin.ignore();
cin.clear();
}
cout << "Check 1" << endl; //executes
cin >> x; //skips
cout << "Check 2" << endl; //executes
cin >> x; //skips
return 0;
}
//end
Instead of the if else, if i put the same concept in a loop
while (!(cin >> x))
the program goes into an infinite loop upon enterring a wrong input.
Please help me explain this phenomenon, as the text book i am following says the code typed above should work as intended.
Thank you
cin is an input stream. If an error occurs cin goes into a let's call it "error occured" state. While in this state no character input can be made, your request to collect a character from the input stream will be ignored. With clear() you clear the error and the input stream stops ignoring you.
Here is the ignore function prototype
istream& ignore ( streamsize n = 1, int delim = EOF );
This function gets characters from the input stream and discards them, but you can't get any character if your stream is ignoring you, so you have to first clear() the stream then ignore() it.
Also, a note on the side: If someone inputs, for example "abc", on the first input request your cin gets only one character that is 'a' and "bc" stays in the buffer waiting to be picked up, but the next call to cin gets the 'b' and 'c' stays in the buffer, so you again end up with an error.
The problem with this example is that the cin.ignore() if no arguments are handed to it only ignores 1 character after you clear(). and the second cin gets 'c' so you still have a problem.
A general solution to this problem would be to call
cin.ignore(10000, '\n');
The first number just has to be some huge number that you don't expect someone would enter, I usually put in 10000.
This call makes sure that you pick up all the characters from the false input or that you pick up every character before the enter was pressed so your input stream doesn't get into the "error occurred" state twice.
You may also want to try
if ( std::cin.fail() )
as a backup to prevent a crash due to input of the wrong type when prompted