How to check for input 'type' ?

Started by
8 comments, last by Drigovas 16 years, 12 months ago
Hello, I am doing several tutorials where I do something like:

#include <iostream>
using namespace std;

int i1;//I just named these i1 and i2 for interger1 and 2
int i2;

int main()
{
    cout<<"Enter an interger: "<<endl;
    cin>>i1;
    cout<<"Enter another interger: "<<endl;
    cin>>i2;
    cout<<i1<<" + "<<i2<<" = "<<i1+i2<<endl;
    return 0;
}
Ok so if I enter a char or float for these two values i1 and i2, the code just skips to the end and says to press any key to continue. So how would I do a 'type check' or a pre-error check, so if they didn't enter an int the program would cycle back and request another number. I know I would use an if or while statement, but what is the expression I would use?

do
{
cout<<"Enter an interger: "<<endl;
cin>>i1;
}
while (i1!=int)
Something like that...
Advertisement
The use of cin with >> when data is in the buffer that is not of the type requested sets 'cin's fail bit, which makes cin completely unusable until the error is dealt with, which is why the program burns straight through to the end when you enter words. Every cin call returns immediately, and does not fill the i1 or i2 with anything at all [does not change the value of the variable].

You can call cin.fail() [which returns true if the value is bad] to test for these errors, and cin.clear() to return cin to a usable state [so it will actually work again]. This failure mechanism means that there is no way that you can get cin dig through the provided data for something useful, thus doing type checking for you in the manner you describe. You CAN do a version of type checking, by reading in various data types [and failing], until you get one that succeeds [do a string last though, since a string can suck up numbers too]

The this approach is demonstrated here.

int main(){	int anInteger;	float aFloat;	string garbage;	bool aNumberWasGotten = false;	while(aNumberWasGotten == false)	{		cin >> anInteger;		if(cin.fail())		{			cin.clear();			cin >> aFloat;			if(!cin.fail())			{				cout << "Enter an INTEGER!, not a float!" << endl;			}			else// float failed			{				cin.clear();				cin >> garbage;				cout << "Enter an INTEGER!, not this... whatever '" << garbage << "' is..." << endl;			}		}		else		{			aNumberWasGotten = true;		}	}	cout << "you entered an integer!, yay!" << endl;	return 0;}


Error checking is always nasty, but when you have the possibility of bad data, it needs to be done.
Best way I know is to read a line into a std::string then use a stringstream to convert the string into an int. That way you can check the error state of the stringstream after the conversion:

#include <string>#include <sstream>#include <iostream>int main(){    std::string s;    std::cout << "Enter an int: ";    std::getline(std::cin,s);    std::istringstream is(s);    int i;    is >> i;    if(is.fail()) std::cout << "You do not have an int!\n";}


It is also worth checking that the stringstream is empty of anything but whitespace after the conversion since if you enter, say, 23.54, the stringstream will put 23 into the int and leave .54 in the stream ready for the next extraction.

I think I check it is empty by doing >> to a std::string and making sure that the stringstream DOES fail.
You can evaluate the stream in a boolean context to determine if it is in a valid state. Note that a failed read places the string in an invalid state. The typical reading code looks like this.

if (std::cin >> i1) { /* Success */ } else { /* Failure */ }


On success, the key is to determine why the reading failed. There are two possible explanations: the data inside the stream was invalid (you entered a non-integer), at which point you may ignore the first few characters, restore the state of the string to a valid one using std::cin.clear(), and read again.

The other explanation is that the end of the stream was reached, in which case std::cin.eof() returns true. You should then handle the fact that no more input will be received.
Ok, so why isn't this working?

//bool gotI1=false, using std, included iostreamwhile(!gotI1)	{		cout<<"Enter an interger: "<<endl;		if(cin>>i1)		{			gotI1=true;		}		else		{			cin.clear();		}	}


I want it to run through once, see if the value in cin returns 'true', if not clear cin and cycle back through.

The problem is that when I enter a char or float, it just keeps failing cin every loop, so it just spams 'Enter an interger: ".

What am I missing?

Keep in mind, I am just doing this to understand error checking, It's not for an assignment or a program. Most of the stuff I will be doing in the future will deal with a user interface and stuff, so I hope error checking won't be such an issues, but I figured I should get used to how it works.

[edit]I guess I have to clear the value stored in i1 ?[/edit]
Your code attempts to read data into an integer. If it fails, it tries again with the exact same stream, which obviously will fail again. This is because neither failure nor clear will alter the data inside the stream.

Once the stream reaches an invalid state, you have to do something about the data it contains, such as removing the first character (which would be invalid), before trying the same reading attempt again.
I am not sure how to do something like that yet. I can just move on and keep reading my tutorial, but if it's a simple command or something could you post an example?

In the &#106avascript code I have been using, I would use something like i1=null. Is there something like that in C++?
Quote:Original post by BUnzaga
In the &#106avascript code I have been using, I would use something like i1=null. Is there something like that in C++?


The stream doesn't care about i1. If reading fails, i1 has not even been modified. The problem here is about the stream contents, not the integer.

Suppose you enter the text "Hello" at the prompt. So, the beginning of standard input is "Hello". Then, std::cin >> i1 attempts to read "Hello" as an integer. It fails, because "Hello" is not a valid integer, which places std::cin in an invalid state, leaves "Hello" in the stream, and does not modify the integer.

Your code then calls std::cin.clear() to notify std::cin that you are aware of the error. Note that the beginning of standard input is still "Hello", as no successful read operation has been performed so far. Then, your loop repeats, and you try to perform std::cin >> i1 again. Note that the contents or the input stream are still "Hello", because you have not done anything with the stream to change its contents, so the reading of "Hello" as an integer fails again.

Your code then calls std::cin.clear() again to notify std::cin that you are aware of the new error. Again, note that the beginning of standard input is still "Hello", as you have still not managed to perform a succesful read operation on your stream. Your loop repeats once more, and the code executes std::cin >> i1 for a third time. The input stream still starts with "Hello", because its contents have still not been altered by a successful read, so reading it as an integer fails once more.

This cycle repeats on and on, without any reason to stop. Basically, your program reaches the situation "the stream contains invalid data", to which you respond "do not worry, try again", where a sane answer would be "do not worry, remove the invalid data, then try again".

Ahh, it makes sense now. So what is the command to erase the information stored in cin? This way when the program loops, it can get fresh information to work with?

I'll look in the help files and see if I can find something, but I just thought I'd ask in case anyone knew off the top of thier heads.

[edit]
Ok so this is what I got, but I'm getting some other error when trying to build it.
#include <iostream>using namespace std;int i1;//I just named these i1 and i2 for interger1 and 2int i2;string s1;float f1;bool gotI1=false;bool gotI2=false;int main(){	while(!gotI1)	{		cout<<"Enter an interger: "<<endl;		if(cin>>i1)		{			gotI1=true;		}		else		{			cin.clear();			if(cin>>f1)			{				cout<<"Enter an interger, not a float!"<<endl;			}			else			{				cin.clear();				if(cin>>s1)				{					cout<<"Enter an interger, not a string!"<<endl;				}			}		}	}/* cout<<"Enter another interger: "<<endl;   cin>>i2;   cout<<i1<<" + "<<i2<<" = "<<i1+i2<<endl;*/    return 0;}


The error I get is:

error C2679: binary '>>' : no operator found which takes a right-hand operand of type 'std::string' (or there is no acceptable conversion)

I'm looking that error up now.

[edit-2]
Ok so I had to use #include<string> I am really new, so I didn't know.

It seems to be working well now, except if I enter a float, it just skips to the end, if I enter a string or character it cycles through as it should, if I enter an int, it works right, but if I enter a floating number it just acts as though the if statements aren't there.

[Edited by - BUnzaga on April 21, 2007 4:12:36 PM]
Quote:Original post by BUnzaga
It seems to be working well now, except if I enter a float, it just skips to the end, if I enter a string or character it cycles through as it should, if I enter an int, it works right, but if I enter a floating number it just acts as though the if statements aren't there.


It depends on what float you are attempting to read. Try '.25' and it will read it as a float, and fail the integer test. Try '12.25' and it'll suck up '12' as an int, and leave '.25' in the bit stream [to cause you a headache later], thus succeeding in the int read, and not triggering your error checking.

These sorts of things are exactly why dealing with user input is such a pain.

This topic is closed to new replies.

Advertisement