c++ – Why would we call cin.clear() and cin.ignore() after reading input?

c++ – Why would we call cin.clear() and cin.ignore() after reading input?

The cin.clear() clears the error flag on cin (so that future I/O operations will work correctly), and then cin.ignore(10000, n) skips to the next newline (to ignore anything else on the same line as the non-number so that it does not cause another parse failure). It will only skip up to 10000 characters, so the code is assuming the user will not put in a very long, invalid line.

You enter the

if (!(cin >> input_var))

statement if an error occurs when taking the input from cin. If an error occurs then an error flag is set and future attempts to get input will fail. Thats why you need

cin.clear();

to get rid of the error flag. Also, the input which failed will be sitting in what I assume is some sort of buffer. When you try to get input again, it will read the same input in the buffer and it will fail again. Thats why you need

cin.ignore(10000,n);

It takes out 10000 characters from the buffer but stops if it encounters a newline (n). The 10000 is just a generic large value.

c++ – Why would we call cin.clear() and cin.ignore() after reading input?

Why do we use:

1) cin.ignore

2) cin.clear

?

Simply:

1) To ignore (extract and discard) values that we dont want on the stream

2) To clear the internal state of stream. After using cin.clear internal state is set again back to goodbit, which means that there are no errors.

Long version:

If something is put on stream (cin) then it must be taken from there. By taken we mean used, removed, extracted from stream. Stream has a flow. The data is flowing on cin like water on stream. You simply cannot stop the flow of water 😉

Look at the example:

string name; //line 1
cout << Give me your name and surname:<<endl;//line 2
cin >> name;//line 3
int age;//line 4
cout << Give me your age: <<endl;//line 5
cin >> age;//line 6

What happens if the user answers: Arkadiusz Wlodarczyk for first question?

Run the program to see for yourself.

You will see on console Arkadiusz but program wont ask you for age. It will just finish immediately right after printing Arkadiusz.

And Wlodarczyk is not shown. It seems like if it was gone (?)*

What happened? 😉

Because there is a space between Arkadiusz and Wlodarczyk.

space character between the name and surname is a sign for computer that there are two variables waiting to be extracted on input stream.

The computer thinks that you are tying to send to input more than one variable. That space sign is a sign for him to interpret it that way.

So computer assigns Arkadiusz to name (2) and because you put more than one string on stream (input) computer will try to assign value Wlodarczyk to variable age (!). The user wont have a chance to put anything on the cin in line 6 because that instruction was already executed(!). Why? Because there was still something left on stream. And as I said earlier stream is in a flow so everything must be removed from it as soon as possible. And the possibility came when computer saw instruction cin >> age;

Computer doesnt know that you created a variable that stores age of somebody (line 4). age is merely a label. For computer age could be as well called: afsfasgfsagasggas and it would be the same. For him its just a variable that he will try to assign Wlodarczyk to because you ordered/instructed computer to do so in line (6).

Its wrong to do so, but hey its you who did it! Its your fault! Well, maybe user, but still…


All right all right. But how to fix it?!

Lets try to play with that example a bit before we fix it properly to learn a few more interesting things 🙂

I prefer to make an approach where we understand things. Fixing something without knowledge how we did it doesnt give satisfaction, dont you think? 🙂

string name;
cout << Give me your name and surname:<<endl;
cin >> name;
int age;
cout << Give me your age: <<endl;
cin >> age;
cout << cin.rdstate(); //new line is here :-)

After invoking above code you will notice that the state of your stream (cin) is equal to 4 (line 7). Which means its internal state is no longer equal to goodbit. Something is messed up. Its pretty obvious, isnt it? You tried to assign string type value (Wlodarczyk) to int type variable age. Types doesnt match. Its time to inform that something is wrong. And computer does it by changing internal state of stream. Its like: You f**** up man, fix me please. I inform you kindly 😉

You simply cannot use cin (stream) anymore. Its stuck. Like if you had put big wood logs on water stream. You must fix it before you can use it. Data (water) cannot be obtained from that stream(cin) anymore because log of wood (internal state) doesnt allow you to do so.

Oh so if there is an obstacle (wood logs) we can just remove it using tools that is made to do so?

Yes!

internal state of cin set to 4 is like an alarm that is howling and making noise.

cin.clear clears the state back to normal (goodbit). Its like if you had come and silenced the alarm. You just put it off. You know something happened so you say: Its OK to stop making noise, I know something is wrong already, shut up (clear).

All right lets do so! Lets use cin.clear().

Invoke below code using Arkadiusz Wlodarczyk as first input:

string name;
cout << Give me your name and surname:<<endl;
cin >> name;
int age;
cout << Give me your age: <<endl;
cin >> age;
cout << cin.rdstate() << endl; 
cin.clear(); //new line is here :-)
cout << cin.rdstate()<< endl;  //new line is here :-)

We can surely see after executing above code that the state is equal to goodbit.

Great so the problem is solved?

Invoke below code using Arkadiusz Wlodarczyk as first input:

string name;
cout << Give me your name and surname:<<endl;
cin >> name;
int age;
cout << Give me your age: <<endl;
cin >> age;
cout << cin.rdstate() << endl;; 
cin.clear(); 
cout << cin.rdstate() << endl; 
cin >> age;//new line is here :-)

Even tho the state is set to goodbit after line 9 the user is not asked for age. The program stops.

WHY?!

Oh man… Youve just put off alarm, what about the wood log inside a water?* Go back to text where we talked about Wlodarczyk how it supposedly was gone.

You need to remove Wlodarczyk that piece of wood from stream. Turning off alarms doesnt solve the problem at all. Youve just silenced it and you think the problem is gone? 😉

So its time for another tool:

cin.ignore can be compared to a special truck with ropes that comes and removes the wood logs that got the stream stuck. It clears the problem the user of your program created.

So could we use it even before making the alarm goes off?

Yes:

string name;
cout << Give me your name and surname:<< endl;
cin >> name;
cin.ignore(10000, n); //time to remove Wlodarczyk the wood log and make the stream flow
int age;
cout << Give me your age: << endl;
cin >> age;

The Wlodarczyk is gonna be removed before making the noise in line 7.

What is 10000 and n?

It says remove 10000 characters (just in case) until n is met (ENTER). BTW It can be done better using numeric_limits but its not the topic of this answer.


So the main cause of problem is gone before noise was made…

Why do we need clear then?

What if someone had asked for give me your age question in line 6 for example: twenty years old instead of writing 20?

Types doesnt match again. Computer tries to assign string to int. And alarm starts. You dont have a chance to even react on situation like that. cin.ignore wont help you in case like that.

So we must use clear in case like that:

string name;
cout << Give me your name and surname:<< endl;
cin >> name;
cin.ignore(10000, n); //time to remove Wlodarczyk the wood log and make the stream flow
int age;
cout << Give me your age: << endl;
cin >> age;
cin.clear();
cin.ignore(10000, n); //time to remove Wlodarczyk the wood log and make the stream flow

But should you clear the state just in case?

Of course not.

If something goes wrong (cin >> age;) instruction is gonna inform you about it by returning false.

So we can use conditional statement to check if the user put wrong type on the stream

int age;
if (cin >> age) //its gonna return false if types doesnt match
    cout << You put integer;
else
    cout << You bad boy! it was supposed to be int;

All right so we can fix our initial problem like for example that:

string name;
cout << Give me your name and surname:<< endl;
cin >> name;
cin.ignore(10000, n); //time to remove Wlodarczyk the wood log and make the stream flow

int age;
cout << Give me your age: << endl;
if (cin >> age)
  cout << Your age is equal to: << endl;
else
{
 cin.clear();
 cin.ignore(10000, n); //time to remove Wlodarczyk the wood log and make the stream flow
 cout << Give me your age name as string I dare you;
 cin >> age;
}

Of course this can be improved by for example doing what you did in question using loop while.

BONUS:

You might be wondering. What about if I wanted to get name and surname in the same line from the user? Is it even possible using cin if cin interprets each value separated by space as different variable?

Sure, you can do it two ways:

1)

string name, surname;
cout << Give me your name and surname:<< endl;
cin >> name;
cin >> surname;

cout << Hello,  << name <<   << surname << endl;

2) or by using getline function.

getline(cin, nameOfStringVariable);

and thats how to do it:

string nameAndSurname;
cout << Give me your name and surname:<< endl;
getline(cin, nameAndSurname);

cout << Hello,  << nameAndSurname << endl;

The second option might backfire you in case you use it after you use cin before the getline.

Lets check it out:

a)

int age;
cout << Give me your age: <<endl;
cin >> age;
cout << Your age is << age << endl;

string nameAndSurname;
cout << Give me your name and surname:<< endl;
getline(cin, nameAndSurname);

cout << Hello,  << nameAndSurname << endl;

If you put 20 as age you wont be asked for nameAndSurname.

But if you do it that way:

b)

string nameAndSurname;
cout << Give me your name and surname:<< endl;
getline(cin, nameAndSurname);

cout << Hello,  << nameAndSurname << endl;
int age;
cout << Give me your age: <<endl;
cin >> age;
cout << Your age is << age << endll

everything is fine.

WHAT?!

Every time you put something on input (stream) you leave at the end white character which is ENTER (n) You have to somehow enter values to console. So it must happen if the data comes from user.

b) cin characteristics is that it ignores whitespace, so when you are reading in information from cin, the newline character n doesnt matter. It gets ignored.

a) getline function gets the entire line up to the newline character (n), and when the newline char is the first thing the getline function gets n, and thats all to get. You extract newline character that was left on stream by user who put 20 on stream in line 3.

So in order to fix it is to always invoke cin.ignore(); each time you use cin to get any value if you are ever going to use getline() inside your program.

So the proper code would be:

int age;
cout << Give me your age: <<endl;
cin >> age;
cin.ignore(); // it ignores just enter without arguments being sent. its same as cin.ignore(1, n) 
cout << Your age is << age << endl;


string nameAndSurname;
cout << Give me your name and surname:<< endl;
getline(cin, nameAndSurname);

cout << Hello,  << nameAndSurname << endl;

I hope streams are more clear to you know.

Hah silence me please! 🙂

Leave a Reply

Your email address will not be published.