This code is part of a "game" that I am developing in Win32:
cout << "You find another deep red apple. Eat or drop?\n";
cin>>noskipws>>sa;
while (sa != "Eat"&&sa != "Drop"&&sa != "eat"&&sa != "drop")
{
cout << "That's not a valid answer. Please choose from the options you were given." << endl;
cin >> sa;
}
And then it enters a loop of saying "That's not a valid answer. Please choose from the options you were given."
How can make the code work so that it reads the string with white spaces without this occurring? I've tried getline() too but it doesn't work.
It's not entirely clear what you want to do and what kind of input you want to accept. For now I'll guess you want to read strings that contain whitespace in a single call. For that, the easiest way is to use std::getline(). You say you tried it and it didn't work, how exactly have you tried it and what didn't work?
Going back to your code, I'll try to explain why it's working the way it does but I won't rewrite it for you, you'll have to try to fix on your own.
Basically the problem is that std::noskipws doesn't help you in the way you expect it to. It indeed sets a flag on the stream that causes it to not skip over whitespace characters when reading from it. So you can do something like this for example:
char c1, c2, c3;
std::cin >> std::noskipws >> c1 >> c2 >> c3;
If you run this and enter "a b c" on the input prompt, the values for the three chars will be 'a', ' ' (space) and 'b'.
If you instead run:
char c1, c2, c3;
std::cin >> c1 >> c2 >> c3;
the values will be 'a', 'b' and 'c'.
But you're not reading individual characters directly, you're reading an std::string. When you do this std::cin >> someString, reading WILL stop at the first whitespace character, no matter whether you have that flag set or not. So if you have this:
std::cin >> std::noskipws >> sa;
and you enter lets say this on the prompt: "not eat or drop". The call above will still read just the first word, "not", into sa. The if will fail, you will print that message and then try to read again from the stream.
And now the fun part starts. At this point the stream contents look like this: " eat or drop". There is a space at the beginning that doesn't get ignored and skipped because you've set that flag. But now the call to operator>>() for strings fails because of that space at the beginning. This sets the fail bit on the stream and doesn't read anything. From this moment on, the stream remains in a failed state, every call to std::cin >> sa from now on fails instantly and silently and you have an infinite loop that keeps printing that message.
See this link for more details on how operator>> works for std::strings.
Later edit
If when you use std::getline() it looks like the first call doesn't wait for input and just returns an empty string immediately, there is an explanation. Check whether somewhere before the call to getline() you do any "normal" input from cin (using operator>>). Let's say before getline() you do something like this (not necessarily in the same function):
SomeType var;
std::cin >> var;
When this asks for input, you probably type something like this on the terminal: value<ENTER>. This reads the value but most likely leaves the new line character in the stream. So the first time you call getline() after this, it will read and discard the new line character and return (probably an empty string).
One way to get around this problem is to do some cleanup on the stream before calling getline(), for example like this:
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
This basically ignores all leftover characters from previous input and allows you to start with a clean stream.
If you want to trim the input string, you can use the boost string algorithms, see the example here
#include <iostream>
#include <boost/algorithm/string.hpp>
int main() {
string sa;
while (std::getline(cin, sa)) {
boost::trim(sa); // remove leading & trailing whitespace
if (sa == "Eat" || sa == "Drop" || sa == "eat" || sa == "drop") {
std::cout << "Option was " << sa << std::endl;
break;
}
std::cout << "That's not a valid answer." << std::endl;
}
return 0;
}
Input (some spaces at begining of line):
WrongInput
Eat
Output:
That's not a valid answer. Please choose from the options you were given.
Option was Eat
There is a little coding involved, replace your while loop with a do-while loop, and use string find function to search for you word.
Next there is the piece of code that could solve your issue, give it a try and rate it if it's good to you.
string sa;
cout << "You find another deep red apple. Eat or drop?\n";
getline(cin, sa);
bool wordFound = false;
do
{
if (sa.find("Eat") != std::string::npos ||
sa.find("Drop") != std::string::npos ||
sa.find("eat") != std::string::npos ||
sa.find("drop") != std::string::npos)
{
wordFound = true;
}
if (wordFound == false)
{
cout << "That's not a valid answer. Please choose from the options you were given." << endl;
cin >> sa;
}
} while (!wordFound);
Related
I thought I understood handling bad input with cin.clear() and cin.ignore(), like it is explained here, but in the following example
#include <iostream>
#include <limits>
using namespace std; //I know that this isn't good practice.
int main () {
int a, b;
while (cout << "Input some int: " && !(cin >> a)) {
cout << "Wrong datatype!\n";
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(), '\n');
}
while (cout << "Input some int: " && !(cin >> b)) {
cout << "Wrong datatype!\n";
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(), '\n');
}
if (a > 1) cout << "Some event.\n";
if (b > 1) cout << "Some other event.\n";
return 0;
}
the behavior I want is only present when the unwanted input is some character.
So if I enter x and y, I will again be asked for two ints and get the appropriate outputs, same if I enter a char and an int two times.
However: If I input, say, 2.3, I will get
Input some int: Wrong datatype!
but won't have a chance to correct my input, since the result invariantly outputs "Some event." The second prompt just accepts the float right away.
What's happening, actually, is the 2 in 2.3 is being accepted by the first prompt, leaving .3 in the input buffer. The Wrong datatype! you are seeing is from your second prompt, seeing a ., which is not a valid character for an integer. You then, I assume, enter an integer which is accepted by your second prompt.
This fundamental approach is fragile, and error-prone.
Your obvious intent is to accept a line of input, and process it. If so, then the correct function to do that is std::getline(). That's what its purpose is. That's exactly what it does. The >> operator does not do that. That's not what it's for. Of course, by using the various auxiliary methods, like ignore(), and clear(), one can still achieve that goal, but, as you've discovered, using those functions correctly is not intuitive. Of course, you can spend copious time pouring over their documentation to understand their every semantic behavior, but why bother, when you can simply use std::getline(), and then move on to something else. It's simply easier to do that.
Of course, once a line of input is received, you would like to parse it into an integer. Now is the correct time to use >> to parse it:
std::string line;
if (std::getline(line, std::cin))
{
std::istringstream i{line};
int n;
if (i >> n)
{
// Input parsed
}
}
Isn't this simpler, more straightforward, and less of a gotcha?. Of course, entering "2.3" here will result in the >> operator parsing the "2", and succeeding, leaving ".3" unparsed. If you would like to detect this situation, simply use get() to see what's left in the std::istringstream. Perhaps accept any trailing whitespace, if you wish.
The problem here is when you enter something like 2.3 to a int cin is okay with that. It reads the 2, sees the . so it stops reading and stores the 2 in the variable and leaves the .3 in the buffer for the next call. So, you pass the first loop, get to the second loop, and then you fail as it tries to read in the . into b. Then you clear the .3 and you can enter another input. If you enter another 2.3 the same thing will happen and b will get 2 and the program continues on.
The "bullet proof" way to read in input is to read it in as a std::string and then parse that to make sure the full input was good. That would look like
std::string line;
while (cout << "Input some int: " && std::getline(cin, line)) {
std::stringstream ss(line);
ss >> a;
if (ss.eof()) // we did consume all the input
break;
else
cout << "Wrong datatype!\n";
}
while (cout << "Input some int: " && std::getline(cin, line)) {
std::stringstream ss(line);
ss >> b;
if (ss.eof()) // we did consume all the input
break;
else
cout << "Wrong datatype!\n";
}
When you input "2.3", cin will stop at '.', and interpret '2' as the desired input.
Then, you will clear cin, when the '.' is encountered, discarding 3.
If you then input a new integer, it will accept it.
Many answers here suggest the use of std::getline and string parsing, either using the string functions or stringstreams. This is quite inefficient and not the way the streams are supposed to be used.
Instead, parse the data when it is still in the input stream:
#include <iostream>
#include <cctype>
#include <limits>
struct read_int {
int& a;
read_int(int& aa) : a{ aa } { }
friend std::istream& operator >>(std::istream& is, read_int& ri) {
char delim;
while(!(is >> ri.a) || (delim = is.get(), delim != '\n' && !std::isspace(delim))) {
std::cerr << "Bad!\n";
is.clear();
is.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
return is;
}
};
int main() {
int a, b;
std::cin >> read_int(a) >> read_int(b);
std::cout << a << ' ' << b;
return 0;
}
This function will accept input like "4 5" or "4\n6" alike, but requests a new input for data like "4.2", discarding everything read before.
I wrote a very basic program in C++ which asked the user to input a number and then a string. To my surprise, when running the program it never stopped to ask for the string. It just skipped over it. After doing some reading on StackOverflow, I found out that I needed to add a line that said:
cin.ignore(256, '\n');
before the line that gets the string input. Adding that fixed the problem and made the program work. My question is why does C++ need this cin.ignore() line and how can I predict when I will need to use cin.ignore()?
Here is the program I wrote:
#include <iostream>
#include <string>
using namespace std;
int main()
{
double num;
string mystr;
cout << "Please enter a number: " << "\n";
cin >> num;
cout << "Your number is: " << num << "\n";
cin.ignore(256, '\n'); // Why do I need this line?
cout << "Please enter your name: \n";
getline (cin, mystr);
cout << "So your name is " << mystr << "?\n";
cout << "Have a nice day. \n";
}
ignore does exactly what the name implies.
It doesn't "throw away" something you don't need. Instead, it ignores the number of characters you specify when you call it, up to the char you specify as a delimiter.
It works with both input and output buffers.
Essentially, for std::cin statements you use ignore before you do a getline call, because when a user inputs something with std::cin, they hit enter and a '\n' char gets into the cin buffer. Then if you use getline, it gets the newline char instead of the string you want. So you do a std::cin.ignore(1000,'\n') and that should clear the buffer up to the string that you want. (The 1000 is put there to skip over a specific number of chars before the specified delimiter, in this case, the '\n' newline character.)
You're thinking about this the wrong way. You're thinking in logical steps each time cin or getline is used. Ex. First ask for a number, then ask for a name. That is the wrong way to think about cin. So you run into a race condition because you assume the stream is clear each time you ask for a input.
If you write your program purely for input you'll find the problem:
int main()
{
double num;
string mystr;
cin >> num;
getline(cin, mystr);
cout << "num=" << num << ",mystr=\'" << mystr << "\'" << endl;
}
In the above, you are thinking, "first get a number." So you type in 123 press enter, and your output will be num=123,mystr=''. Why is that? It's because in the stream you have 123\n and the 123 is parsed into the num variable while \n is still in the stream. Reading the doc for getline function by default it will look in the istream until a \n is encountered. In this example, since \n is in the stream, it looks like it "skipped" it but it worked properly.
For the above to work, you'll have to enter 123Hello World which will properly output num=123,mystr='Hello World'. That, or you put a cin.ignore between the cin and getline so that it'll break into logical steps that you expect.
This is why you need the ignore command. Because you are thinking of it in logical steps rather than in a stream form so you run into a race condition.
Take another code example that is commonly found in schools:
int main()
{
int age;
string firstName;
string lastName;
cout << "First name: ";
cin >> firstName;
cout << "Last name: ";
cin >> lastName;
cout << "Age: ";
cin >> age;
cout << "Hello " << firstName << " " << lastName << "! You are " << age << " years old!" << endl;
}
The above seems to be in logical steps. First ask for first name, last name, then age. So if you did John enter, then Doe enter, then 19 enter, the application works each logic step. If you think of it in "streams" you can simply enter John Doe 19 on the "First name:" question and it would work as well and appear to skip the remaining questions. For the above to work in logical steps, you would need to ignore the remaining stream for each logical break in questions.
Just remember to think of your program input as it is reading from a "stream" and not in logical steps. Each time you call cin it is being read from a stream. This creates a rather buggy application if the user enters the wrong input. For example, if you entered a character where a cin >> double is expected, the application will produce a seemingly bizarre output.
Short answer
Why? Because there is still whitespace (carriage returns, tabs, spaces, newline) left in the input stream.
When? When you are using some function which does not on their own ignores the leading whitespaces. Cin by default ignores and removes the leading whitespace but getline does not ignore the leading whitespace on its own.
Now a detailed answer.
Everything you input in the console is read from the standard stream stdin. When you enter something, let's say 256 in your case and press enter, the contents of the stream become 256\n. Now cin picks up 256 and removes it from the stream and \n still remaining in the stream.
Now next when you enter your name, let's say Raddicus, the new contents of the stream is \nRaddicus.
Now here comes the catch.
When you try to read a line using getline, if not provided any delimiter as the third argument, getline by default reads till the newline character and removes the newline character from the stream.
So on calling new line, getline reads and discards \n from the stream and resulting in an empty string read in mystr which appears like getline is skipped (but it's not) because there was already an newline in the stream, getline will not prompt for input as it has already read what it was supposed to read.
Now, how does cin.ignore help here?
According to the ignore documentation extract from cplusplus.com-
istream& ignore (streamsize n = 1, int delim = EOF);
Extracts characters from the input sequence and discards them, until
either n characters have been extracted, or one compares equal to
delim.
The function also stops extracting characters if the end-of-file is
reached. If this is reached prematurely (before either extracting n
characters or finding delim), the function sets the eofbit flag.
So, cin.ignore(256, '\n');, ignores first 256 characters or all the character untill it encounters delimeter (here \n in your case), whichever comes first (here \n is the first character, so it ignores until \n is encountered).
Just for your reference, If you don't exactly know how many characters to skip and your sole purpose is to clear the stream to prepare for reading a string using getline or cin you should use cin.ignore(numeric_limits<streamsize>::max(),'\n').
Quick explanation: It ignores the characters equal to maximum size of stream or until a '\n' is encountered, whichever case happens first.
When you want to throw away a specific number of characters from the input stream manually.
A very common use case is using this to safely ignore newline characters since cin will sometimes leave newline characters that you will have to go over to get to the next line of input.
Long story short it gives you flexibility when handling stream input.
Ignore function is used to skip(discard/throw away) characters in the input stream. Ignore file is associated with the file istream.
Consider the function below
ex: cin.ignore(120,'/n');
the particular function skips the next 120 input character or to skip the characters until a newline character is read.
As pointed right by many other users. It's because there may be whitespace or a newline character.
Consider the following code, it removes all the duplicate characters from a given string.
#include <bits/stdc++.h>
using namespace std;
int main() {
int t;
cin>>t;
cin.ignore(); //Notice that this cin.ignore() is really crucial for any extra whitespace or newline character
while(t--){
vector<int> v(256,0);
string s;
getline(cin,s);
string s2;
for(int i=0;i<s.size();i++){
if (v[s[i]]) continue;
else{
s2.push_back(s[i]);
v[s[i]]++;
}
}
cout<<s2<<endl;
}
return 0;
}
So, You get the point that it will ignore those unwanted inputs and will get the job done.
It is better to use scanf(" %[^\n]",str) in c++ than cin.ignore() after cin>> statement.To do that first you have to include < cstdio > header.
Sorry, I'm sure this question gets asked a million times. I've searched, and I've found answers for "Press ENTER to continue", but for the life of me, I can't find the answer to my question.
do{
//something something
cout<<"Press ENTER to continue or anything else to quit."<<endl;
//Enter is pressed or something else
}while(Enter is pressed)
here's what I'm trying to use it for
do{
cout<<"Enter value: ";
cin>>value[n];
cout<<"Enter 'Y' to continue, anything else to quit"<<endl;
cin>>reply;
}while(reply == 'y' || reply == 'Y')
In place of "Enter 'Y' to continue. Anything else to quit." I just want an "Enter to continue, Q to quit".
edit: After implementing what Tony D showed, I now have
do{
cout<<"Enter the person's first name, last name, and age."<<endl;
cin>>list[n].firstName>>list[n].lastName>>list[n].age;
n++;
cout<<"Press ENTER to continue. 'Q' to quit. ";
std::getline(std::cin, reply);
}while (std::getline(std::cin, reply) && reply != "Q" && reply != "q");
it works, but I feel like something is still wrong. If I do just while (std::getline(std::cin, reply) && reply != "Q" && reply != "q");
Q or q won't exit the loop and Enter just takes me to the next line. I tried this as a do while and a while loop.
Firstly, when you do streaming like this (adding error handling)...
if (!(cin>>list[n].firstName>>list[n].lastName>>list[n].age))
{
std::cerr << "oops... unable to parse `firstname lastname age' from stdin... terminating\n";
return EXIT_FAILURE;
}
...the parsing of input for age terminates on and does not consume the first whitespace character following. That means the terminating newline will always be left on the cin stream. So, you need to do something to get rid of it. A simple way to do that while making it clear that you're not interested in the rest of the line's content is:
std::cin.ignore(std::numeric_limits<streamsize>::max(), '\n');
If you prefer to check everything really carefully, you could use std::getline(std::cin, line) to read the line of input into a std::string, then construct a std::stringstream with it, then parse out the content like this:
std::string line;
if (cin>>list[n].firstName>>list[n].lastName>>list[n].age))
{
std::istringstream iss(line);
if (!(iss >> firstName >> lastName >> age))
{
std::cerr << "oops... unable to parse `firstname lastname age' from stdin... terminating\n";
return EXIT_FAILURE;
}
char c;
if (iss >> c) // this should fail - no more non-whitespace content allowed...
{
std::cerr << "oops... extra input encountered after the age value... terminating\n";
return EXIT_FAILURE;
}
}
Once we're sure you're reading the entire line of per-person input, for the loop termination itself it's probably easiest to use:
do
{
...
} while (std::getline(std::cin, my_string) && my_string != "Q" && my_string != "q");
That way, any newline characters will automatically be removed from the stream. Using std::cin.get() to read a single character from the stream is possible and more efficient, but it's a little trickier handling the newline. Can get fancier if you want to ignore leading whitespace, complain about other non-empty input etc..
Whatever you type may not reach the stream until you press enter, so they'll need to press 'Q' to quit. To avoid that, you'd need OS-specific non-Standard code or libraries. There are plenty of SO questions explaining how to read keys without waiting for enter.
If you need to tolerate errors and prompt the user to try again, the easiest approach is to read in a way that can't fail due to invalid input, then use a separate stream object when you're attempting the parsing that may fail. The following shows how to parse a few variables out of a line of input:
std::string line;
while ((std::cout << "enter x y z (space separated): ") &&
std::getline(std::cin, line))
{
std::istringstream iss(line); // input string stream with contents of last line read
char c;
if ((iss >> x >> y >> z) && !(iss >> c))
// we successfully parsed out x, y and z, and there was no trailing
// non-whitespace character, so all's good...
break;
std::cerr << "unable to parse 'x', 'y' and 'z' from input, or unexpected trailing text\n";
std::cerr << "please try again\n";
}
This way, iss may enter a bad state due to merely bogus user input but is recreated with the next line's content anyway. std::cin only fails on unrecoverable issues (end-of-file, hardware error etc). That avoids having to reset any stream state, which is a painful thing to have to do, see http://support.microsoft.com/kb/132422 for an example program showing how.
For some weird reason my input line cin.getline(oneLine, 80); is completely ignored when I put it in this else if block. I can't understand why because when I move it somewhere else in the program, it works.
else if (choice == "user-id")
{
cout << endl << "Enter a full name e.g. John Smith ";
char oneLine[80];
cin.getline(oneLine, 80);
cout << oneLine;
}
Here's the rest of my code. I'm new to C++ so I'm sure a lot of my conventions may be questionable at best.
int main( )
{
while (true)
{
int pause;
string choice = "proceed";
string nameGiven;
string userIdGiven;
string result;
using namespace std ;
while ((choice != "name") && (choice != "user-id"))
{
cout << "Would you like to search for a name or user-id? ";
cin >> choice;
if ((choice != "name") && (choice != "user-id"))
cout <<"Please enter a valid choice (name or user-id)" << endl;
}
if (choice == "name")
{
string dataType = "int";
while (true)
{
cout << endl << "Enter a valid user id (4 digit maximum) ";
cin >> userIdGiven;
if (valid(userIdGiven))
break;
else
cout << endl << "Not a valid number. " << endl;
continue;
}
result = findData(userIdGiven, dataType);
cout << "name: " << result;
}
else if (choice == "user-id")
{
cout << endl << "Enter a full name e.g. John Smith ";
char oneLine[80];
std::getline(oneLine, 80);
cout << oneLine;
}
string ans;
cout << endl << "Would you like to play again? (yes/no) " << endl;
cin >> ans;
if ( (ans == "yes") || (ans == "Yes") || (ans == "Y") || (ans == "y") )
continue;
else
break;
cin >> pause;
}
return 0;
}
Your std::cin object is in a bad state (std::cin.good() == false) from a previous input operation. For example, you might have tried to read a number, but there were only nun-numeric characters in the input buffer.
Always check for input success before continuing using a std::istream.
Note: Don't use the old input functions operating with char*, as they are more complicated and less safe to use than the new ones operating on std::string. In your case, use std::getline(std::istream&, std::string&, char = '\n').
twsaef's comment's substantively correct... you're streaming a string into choice, which consumes the characters up until but excluding the next whitespace character - you're probably typing a newline to terminate your input, so it's left in the buffer. Then you use getline which sees that newline and reads an empty string.
Easiest solution is to call getline() to read the initial string too, then check if choice is "name\n" or "user-id\n". Better - write a "trim" function to remove the whitespace from the line before comparison (boost string library has this already). Otherwise, you could use read and ignore characters from std::cin until you get a '\n'. Or even read a line then put in into a stringstream and read a string from there.... Lots of choices.
And, please check your stream state! Try to use:
if (std::cin >> x)
// x was parsed from stream... use it
else
// print an error so you know where things failed!
FWIW, I guessed what the problem would be (it's stupidly common) before seeing the update, and chuckled to myself at the other guesses (although they make very good points even if they missed the OP's problem).
The line of code is working correctly and as advertised. It doesn't happen to be working the way you want it to.
When you read from std::cin, that does not pause the program and wait for input. What causes the pause is the lack of sufficient input data for the read operation.
Input is fed to your program a line at a time. Remember, the console window is a program, too. It is responsible for translating the user's key-presses into text characters (bytes, really), handling things like the backspace key, and gathering it all up into lines.
So say you read an int with operator>>, and then read a line with getline. The program will not see an int until the user hits the Return key, because that triggers the console to feed a line of input to your program.
operator>> will skip leading whitespace, read the integer, and leave trailing whitespace alone. Newline characters are whitespace. There is a newline character in the input (at the end of the line, obviously).
getline() will not skip any leading whitespace, and read until the next newline. The very next character happens to be a newline, so getline() happily reads an empty line and the program proceeds with that.
So how do you fix that? Chances are, if you're reading all your input from cin, that you want the program to pause every time you come to a reading operation. The way to do that is to ensure that there is never any available data at that point, and the way to do that is to read everything that's available - i.e., the whole line - every time that you read something.
So, always read a full line from cin. As noted by wilx, please use the free function std::getline for this. Do not use the .getline member function of the stream. Use std::string to represent strings of text. That's what it's there for. It will make your life much, much easier. That also means that if you're expecting an integer and the user types "76 trombones", you get rid of the "trombones" data (and can decide whether you want to just throw it away, or yell at the user and make him re-enter a number without any funny commentary).
But then what? You just have a string, where you may have wanted an int. Fortunately, there is a simple solution for that. We can treat the string as a source of stream data, using the standard library class std::stringstream. We just construct a stringstream from the string, and then use it just like std::cin - i.e. we can read from it with operator>>, check the stream state to see if reading was successful, etc.
As noted by sbi, always check whether reading succeeded! If you try to read an int and the stream contains text like "hi mom", then (a) the int variable will not be altered (so if it was uninitialized it is still uninitialized, a very dangerous state to be in), and (b) the stream will go into a "failed" state and will not read any more until you clear it, and (c) even if you clear it, the data will still be there, which can trigger an infinite loop if you're not careful.
Fortunately, with the separate stringstream, we avoid all kinds of complications. If reading fails, then all those things happen to the stringstream object - not to std::cin. The getline operation will always succeed on std::cin unless perhaps the user explicitly indicates an end-of-file (control-D character on Linux, or control-Z on Windows). We can easily check if the stringstream is in the failed state, loop and just create another one - the old one will automatically get cleaned up.
We can even make a helper function like:
template <typename T>
// Attempt to read into to_read, and return whether successful.
bool read_primitive_from_input(std::istream& input, T& to_read) {
std::string line;
std::getline(std::cin, line);
std::istringstream iss(line);
return iss >> to_read;
}
std::stringstream is provided by the standard library header <sstream>. std::string comes from <string>, of course.
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
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. :)
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' );
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
When you insert a letter this happens:
operator>> extracts characters from the stream and try to convert them to a number;
it fails in the conversion, so it sets the stream state to 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);
since it returned and (probably) opt is left as it is, the loop continues;
when the execution returns to std::cin >> opt;, operator>> sees that the state is still ios::failbit, so it doesn't even try to extract anything;
goto 3.
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