Reputation: 7813
I'm doing a console app, I'm passing an integer to the app and it works ok, but if I pass a letter, it goes crazy,
int opt=0;
std::cout<<"Pick lang:"<<'\n';
std::cout<<"1.[es-ES]:"<<'\n';
std::cout<<"2.[en-US]:"<<'\n';
std::cin >> opt;
while(opt<1 || opt>2)
{
std::cout<<"\nERROR!"<<'\n';
std::cout<<"Pick lang again:"<<'\n';
std::cout<<"1.[es-ES]:"<<'\n';
std::cout<<"2.[en-US]:"<<'\n';
std::cin >> opt;
}
I tried to use isdigit() but I get the same result. Thanks
Upvotes: 2
Views: 1816
Reputation: 208436
The problem is that the call std::cin >> opt
is failing to parse the character and returns immediatly (without consuming the buffer), then it finds the same contents and fail....
You should check the result of the operation and react to it. One possibility would be checking the fail bit (std::cin.fail()
) and failing the whole operation or consuming parts of the buffer (maybe a a single character, maybe more, depending on how you want the application to behave).
The simplest thing would probably be not reading into a number, but rather a character, and then comparing with the expected character:
char opt = 0;
do {
// prompt user for input
if (! (std::cin >> opt) ) {
// io error, report and bail out
break;
}
} while ( opt != '0' && opt != '1' );
Upvotes: 2
Reputation: 20858
After performing cin >> extraction, you want to check if the cin stream is still good or not. If you expect cin to extract a number but it gets something else instead, eg. like a letter, then the stream will be set to a bad state and that's why you see it 'going crazy'.
What you have to do is after input, check if cin is still good. If it's in a bad state, you need to clear its flags and then remove out any of the junk data in the stream. If you don't, then subsequent uses of cin will simply fail to function.
Taking your code snippet for example, you can change it to something like this:
int opt = 0;
bool inputGood = false;
do
{
std::cout << "Pick lang again:" << '\n';
std::cout << "1.[es-ES]:" << '\n';
std::cout << "2.[en-US]:" << '\n';
inputGood = std::cin >> opt;
if(!inputGood)
{
std::cout << "\nERROR! Invalid choice." << '\n';
cin.clear();
while( cin.get() != '\n' );
}
}while(!inputGood || opt < 1 || opt > 2);
Edit: whoops minor error in the cin error handling. Corrected and should be working now. :)
Upvotes: 7
Reputation: 126867
When you insert a letter this happens:
operator>>
extracts characters from the stream and try to convert them to a number;ios::failbit
and returns; opt
probably is untouched (the standard delegates this stuff to the locale library, which is a zone of C++ that I never really understood - for the brave enough, it's at §22.2.2.1.2);opt
is left as it is, the loop continues;std::cin >> opt;
, operator>>
sees that the state is still ios::failbit
, so it doesn't even try to extract anything;To fix the problem, you should clean the error state and remove the "wrong" characters from the input buffer. Since you probably don't want to add all that code to every cin>>
, it's useful to create a function to deal with this common problem; personally, I created this little header (AcquireInput.hpp
) that has proven useful many times:
#ifndef ACQUIREINPUT_HPP_INCLUDED
#define ACQUIREINPUT_HPP_INCLUDED
#include <iosfwd>
#include <limits>
#include <string>
template<typename InType> void AcquireInput(std::ostream & Os, std::istream & Is, const std::string & Prompt, const std::string & FailString, InType & Result)
{
do
{
Os<<Prompt.c_str();
if(Is.fail())
{
Is.clear();
Is.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
Is>>Result;
if(Is.fail())
Os<<FailString.c_str();
} while(Is.fail());
}
template<typename InType> InType AcquireInput(std::ostream & Os, std::istream & Is, const std::string & Prompt, const std::string & FailString)
{
InType temp;
AcquireInput(Os,Is,Prompt,FailString,temp);
return temp;
}
/* Usage example:
//1st overload
int AnInteger;
AcquireInput(cout,cin,"Please insert an integer: ","Invalid value.\n",AnInteger);
//2nd overload (more convenient, in this case)
int AnInteger=AcquireInput(cout,cin, "Please insert an integer: ","Invalid value.\n");
*/
#endif
Upvotes: 1
Reputation: 11514
Reading in numbers directly is problematic
If std::cin is presented with input it cannot process, std::cin goes into a "fail" state The input it cannot process is left on the input stream.
All input will be ignored by std::cin until the "fail" state is cleared: std::cin.clear()
A routine that reads a number directly should:
Read in the number
Check to see that the input stream is still valid
If the input stream is not good (!std::cin)
- Call std::cin.clear() to take the stream out of the "fail" state.
- Remove from the stream the input that caused the problem: std::cin.ignore(...)
- Get the input again if appropriate or otherwise handle the error
more info here: http://www.augustcouncil.com/~tgibson/tutorial/iotips.html
Upvotes: 1