C++: input using cin command and a loop

Started by
4 comments, last by codejosh94 14 years, 5 months ago
Hello guys, I'm reading a C++ game development book called Learn C++ by Making Games. So far, I do input from the user using the cin command. Sometimes I use a do-while loop to validate this input, for example if I need a 'Y' for yes and 'N' for no. I'm currently working on adding other features to a game of Blackjack (a console program, it's all I've learned so far ^^). It seems that I have to declare a char variable to hold the player's input(in this case, a character 'Y' or a 'N') outside a do-while loop I use for validation. Because if I declare it inside, and I input an invalid value( such as a 'z' ) the loop runs twice, displaying an error message each time, although it should as for input at every running of the loop. When I took the char variable's declaration out of the loop, so that it came before the loop, it worked fine. Here's the code: CODE----------------------------------------------------------------- // Make sure the input is valid bool validEntry = false; char answer; do { // Let's ask the player if he wants to bid for his hand. cout << "Would you like to bid on this round? [Y]es or [N]o." << endl; cin >> answer; answer = toupper( answer ); switch( answer ) { case 'Y': isBetting = true; case 'N': validEntry = true; break; default: cout << "Invalid entry; please try again." << endl; break; } } while ( !validEntry ); // Repeat as long as necessary ------------------------------------------------------------------------------- As you can see, the do-while loop inputs a character from the user and makes it an upper-case letter ( using the toupper() function ) and uses a switch statement to decide whether the player has input a valid entry(in this case, a 'y' or a 'n'). If he hasn't, the switch statement displays an error message and the do-while loop runs again, starting with the 'Would you like to bid" message. I use a boolean variable called validEntry to decide whether to run the loop again. Here's the output when I run the program: OUTPUT---------------------------------------------------------------------------------------------------------------------------------------------------------- The player has total $20 bucks. Would you like to bid on this round? [Y]es or [N]o. j Invalid entry; please try again. Would you like to bid on this round? [Y]es or [N]o. -------------------------------------------------------------------------------- It works just as it should; If the player has input something invalid, as shown here ( he inputs a 'j' ) the message "Invalid entry; please try again" appears and the program prompts the player again for valid input. Look at the code again, and you'll see that the declaration of the char variable that holds the player's input is declared BEFORE the input do-while loop. However, if I put the declaration of the variable INSIDE the loop itself, it outputs erratically, at least abnormally. When the code is like this: CODE---------------------------------------------------------------------------- bool validEntry = false; do { // Let's ask the player if he wants to bid for his hand. cout << "Would you like to bid on this round? [Y]es or [N]o." << endl; char answer; cin >> answer; answer = toupper( answer ); switch( answer ) { case 'Y': isBetting = true; case 'N': validEntry = true; break; default: cout << "Invalid entry; please try again." << endl; break; } } while ( !validEntry ); // Repeat as long as necessary ------------------------------------------------------------------------------- I say, when the loop contains the declaration of the char variable answer, the program outputs like this: OUTPUT---------------------------------------------------------------------------------------------------------------------------------------------------------- The player has total $20 bucks. Would you like to bid on this round? [Y]es or [N]o. j Invalid entry; please try again. Would you like to bid on this round? [Y]es or [N]o. Invalid entry; please try again. Would you like to bid on this round? [Y]es or [N]o. ------------------------------------------------------------------------------- As you can see, there's something wrong here. The loop displays the input-prompting message, but does not prompt for it until it runs again. This is not normal. Could you explain to me what's wrong? I have fixed the bug, but I would still like an explanation, if you can. Thank you in advance, Regards, Josh, a fledgling programmer
Advertisement
bool validEntry = false;
do {
// Let's ask the player if he wants to bid for his hand.
cout << "Would you like to bid on this round? [Y]es or [N]o." << endl;
char answer = '\0'; //try that? I forget much about C++ but I believe the value will still be set from the loop before?
cin >> answer;
answer = toupper( answer );
switch( answer )
{
case 'Y':
isBetting = true;
case 'N':
validEntry = true;
break;
default:
cout << "Invalid entry; please try again." << endl;
break;
}
} while ( !validEntry ); // Repeat as long as necessary
std::cin only requests input from the user when its input buffer is full. When the user enters Y\n", or "z\n", only a character is extracted into answer leaving the rest, "\n", in the buffer. The newline character '\n' is extracted the next time around. Try inputting "asdf", "abc", and "ab". The loop should execute 5, 4, and 3 times (respectively) "abc", "a b c" and "a bc". The loop should execute 3 times on each. This is one of the "gotchas" in C++ you have to look out for. To avoid this, either get the entire line of input using std::getline in <string> or use std::cin.ignore().

As a rule of thumb, you should always extract a line at a time from cin:
#include <iostream>#include <string>bool GetYesNoResponse( const std::string& prompt ) {	using namespace std;		string response;	bool validResponse = false;	do { 		cout << prompt << " (y/n) ";				getline( cin, response ); // Gets a line of input from cin, stores it in response				validResponse = response.length() != 1			&& response[0] != 'Y' && response[0] != 'y'			&& response[0] != 'N' && response[0] != 'n';				if ( !validResponse ) 			cout << "Invalid input\n";	} while ( cin && validResponse );	return cin && response[0] == 'y' || response[0] == 'Y';}int main() {	do {		// Main loop here	} while( GetYesNoResponse( "Play again?" ) && cin );}


EDIT:
cin >> char does not leave the newline in the input buffer

[Edited by - _fastcall on November 8, 2009 3:55:09 AM]
Quote:Original post by _fastcall
std::cin only requests input from the user when its input buffer is full. When the user enters "Y\n", or "z\n", only a character is extracted into answer leaving the rest, "\n", in the buffer. The newline character '\n' is extracted the next time around. Try inputting "asdf", "abc", and "ab". The loop should execute 5, 4, and 3 times (respectively). This is one of the "gotchas" in C++ you have to look out for. To avoid this, either get the entire line of input using std::getline in <string> or use std::cin.ignore().

As a rule of thumb, you should always extract a line at a time from cin:
*** Source Snippet Removed ***


That makes sense, but when I entered "asdf" into the loop shown in my first question, the loop only executed 4 times, exactly. Why is that?

Regards
Sorry, I misread your question and cin >> char does not leave the newline in the input buffer :P

Anyways, if you change it like so, you should get the results you're looking for:
do {	// Let's ask the player if he wants to bid for his hand.	cout << "Would you like to bid on this round? [Y]es or [N]o." << endl;		string input	getline( cin, input );	if ( input.length() == 1 ) {		answer = input[0]; // or toupper( input[0] )		switch( answer )		{			case 'y':			case 'Y':				isBetting = true;			case 'n':			case 'N':				validEntry = true;				break;			default:				cout << "Invalid entry; please try again." << endl;				break;		}	} else		cout << "Invalid entry; please try again." << endl;} while ( !validEntry ); // Repeat as long as necessary


When you compare the changes, cin >> answer allows the user to press enter, add whitespace, press enter again, and continue until you give cin a non-whitespace char, whereas getline immediately returns on the return key.
Oh I think I understand, _fastcall. Thanks for your reply. I only have some C# and Qbasic (and very little at that-I mean the qbasic) experience, and I've just started on C++. Again, thanks for the replies.
Regards

This topic is closed to new replies.

Advertisement