cin.getline not executing c++ - c++

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.

Related

C++ white line skipping error

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

Integer input validation - Rejecting non integer input and requesting another in C++?

This function keeps getting called in another function inside a while-loop while valid_office_num is false. The problem is that if the input begins with a digit but is followed by other invalid characters (e.g. 5t) it takes the digit part and accepts that as a valid input. I want it to consider the whole input and reject it so it can ask for another one. I thought I could use getline() but then I cannot use cin.fail(). How could I implement this behavior?
I forgot to mention I am very new to C++, I have only learnt the basics so far.
(To be clear the desired behavior is to reject anything that contains anything other than digits. This is not an integer range check question. If it is NOT an integer, discard it and request another one)
//Function to read a valid office number, entered by user
int read_office_num()
{
//Declaration of a local variable
int office_num;
//Read input
cin >> office_num;
//Check if input was valid
if (cin.fail())
{
//Print error message
cout << "\nInvalid office number, it should only consist of digits!! Enter another:\n";
//Clear error flags
cin.clear();
//Ignore any whitespace left on input stream by cin
cin.ignore(256, '\n');
}
else
{
//Office number entered is valid
valid_office_num = true;
}
return office_num;
}
From what I gather you want the whole line to be read as a number and fail otherwise?
Well, you can use std::getline(), but you have to follow the algorithm below (I will leave the implementation to you..)
use std::getline(cin, str) to read a line, and if this returns true
use std::stoi(str, &pos) to convert to integer and get the position of the last integer
if pos != str.size() then the whole line in not an integer (or if the above throws an exception), then it's not a valid integer, else return the value...
Read a line of input as a std::string using std::getline().
Examine the string and check if it contains any characters that are not digits.
If the string only contains digits, use a std::istringstream to read an integer from the string. Otherwise report a failure, or take whatever other recovery action is needed (e.g. discard the whole string and return to read another one).
You could use a stringstream
int read_office_num()
{
//Declaration of a local variable
int office_num;
string input = "";
while (true) {
getline(cin, input);
stringstream myStream(input);
if (myStream >> office_num)
break;
cout << "\nInvalid office number, it should only consist of digits!! Enter another:\n" << endl;
}
return office_num;
}
If you want to reject input like 123 xxx you could add an additional check to verify that the received string is indeed an integer:
bool is_number(const string& s)
{
string::const_iterator itr = s.begin();
while (itr != s.end() && isdigit(*itr)) ++itr;
return !s.empty() && itr == s.end();
}
int read_office_num()
{
//Declaration of a local variable
int office_num;
string input = "";
while (true) {
getline(cin, input);
stringstream myStream(input);
if (is_number(input) && myStream >> office_num)
break;
cout << "\nInvalid office number, it should only consist of digits!! Enter another:\n" << endl;
}
return office_num;
}
You should probably just look at the number of input characters that are left in cin. You can do that with in_avail
Your function will probably end up having a body something like this:
//Declaration of a local variable
int office_num;
//Read input and check if input was valid
for (cin >> office_num; cin.rdbuf()->in_avail() > 1; cin >> office_num){
//Print error message
cout << "\nInvalid office number, it should only consist of digits!! Enter another:\n";
//Ignore any whitespace left on input stream by cin
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
//Office number entered is valid
valid_office_num = true;
return office_num;
Points of interest:
There is always at least 1 character in cin otherwise the cin would be marked as bad and that would not be good
You don't need valid_office_num if read_office_num is implemented this way, cause valid_office_num will always be set to true before returning
Hm. I may be missing something, but why not read a line, trim it, use regular expressions to validate a number and then exploit strstream's facilities or just atoi if you must? In all reality I'd probably just let users get away with extraneous input (but discard it if I'm sure I'm always running interactively). Following the motto "be lenient in what you accept."
The "interactive" caveat is important though. One can generally not assume that cin is a terminal. Somebody may get cocky and let your program run on a text file or in a pipeline, and then it would fail. A robust approach would separate data processing (portable) from means of input (possibly machine specific and therefore also more powerful and helpful than stdin/stdout via a console).
Here's how to do it using Boost Lexical Cast:
#include <boost/lexical_cast.hpp>
#include <iostream>
#include <vector>
#include <string>
int read_office_num()
{
using boost::lexical_cast;
using boost::bad_lexical_cast;
using namespace std;
int office_num;
while (true)
{
try
{
string input = cin.getline();
office_num = lexical_cast<int>(*argv));
break;
}
catch(const& bad_lexical_cast)
{
cout << "\nInvalid office number, it should only consist of digits!! Enter another:\n";
}
}
return office_num;
}

how do you "Press ENTER to continue, anything else to quit" in C++

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.

getline() not waiting for input inside switch...case (C++)

I'm trying to get input from the user using getline().
The following code works fine. It waits for the user to type the file name and stores it in fileName.
#include <iostream>
#include <string>
using namespace std;
string inputFile();
int main()
{
string fileName;
cout << "Enter the name of the file including the path" << endl;
getline(cin, fileName);
return 0;
}
However, this code does not work fine.
#include <iostream>
#include <string>
using namespace std;
string inputFile();
int main()
{
int option;
cout << "Enter option number" << endl;
cin >> option;
switch (option)
{
case 1:
{
string fileName;
cout << "Enter the name of the file including the path" << endl;
getline(cin, fileName);
break;
}
case 2:
cout << "You chose option 2";
break;
case 3:
cout << "You chose option 3";
break;
default:
cout << "value unknown";
}
return 0;
}
After the user enters 1 and the program enters the switch...case, the user is again asked for the file name. But, this time the program does not wait for a response.
Why is getline() not working the way it would outside the switch...case structure?
Any suggestions would be greatly appreciated.
cin leaves the newline character (\n) in the stream. cin.ignore() extracts and discards characters. It can be used to flush the cin until \n is reached.
So, the solution is to add std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); , before the 2nd call to getline(cin, fileName);
Also add cin.clear() to remove the error flag on cin
Example:
case 1:
{
std::string fileName;
std::cout << "Enter the name of the file including the path" << std::endl;
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
getline(std::cin, fileName);
break;
}
The problem might be, that you are reading the number of the case via >> into the integer variable. By doing that, the newline produced by the enter key is still in the buffer. Now getline tries to read from the input stream and instantly receives a newline.
And because it only reads upon the next newline, it exits.
The problem has nothing to do with the switch-statement! Instead, it has to do with mixing formatted input (using operator>>()) and unformatted input (in this case std::getline()): the formatted input stops reading as soon as a character doesn't match the format. When reading an integer, it stops as soon as a non-digit is found. That is, any newline entered after the number will be stuck in the stream and std::getline() will happily take this newline as an opportunity to stop its input.
When switching between formatted and unformatted input, you normally want to get rid of whitespaces. For example, you could use
if (std::getline(std::cin >> std::ws, fileName)) { ... }
to first skip any whitespace, then attempt to read fileName and, if this is successful, process the input (input always needs to be checked for success).
You are mixing formatted input and line input on a single stream. This is a bad idea, use either formatted input operator>> or line input std::getline() never both on the same stream.
This is basically because of the way white space is handled. In this case you are leaving a \n character on the input when you read the option.
cin >> option;
This reads the option, but leaves anything after the option (after the integer) on the input stream (including the \n character). Thus the next use of std::getline() simply reads upto the \n character (which probably gives you zero characters).
Interactive user input is line based.
Especially since the std::cin stream is buffered (and thus not flushed until you hit return).
So when I read input from an interactive user I always read a line of text at a time. Then parse this line for what I am looking for.
std::string optionLine;
std::getline(std::cin, optionLine);
int option = boost::lexical_cast<int>(optionLine);
Note: You don't need boost::lexical_cast, you can achieve the same affect with std::stringstream and another variable.
// A simple alternative to boost::lexical_cast
// Not quite as pedantic as above but you can add more tests as required.
std::stringstream optionLineStream(optionLine);
int option;
if (optionLineStream >> option)
{
// reading the option worked.
}
Reading a line at a time and then parsing the input also has the advantage that you never put the input into a bad state and need to reset it (any bad state is set on intermediate std::stringstream objects). So fixing erroneous user input is easier.

c++ validate number and stop infinity loop

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