My first program in C++ is a command line survival game. I have a switch statement in a while loop, but it always goes through once and runs the default choice before getting the users input. Here's the relevant code:
int menu() {
while (true) {
char choice = getChoice();
switch(choice) {
case 'p':
{
system("clear");
Arena arena(player, difficulty);
arena.play();
}
break;
case 'u':
{
system("clear");
player.save();
player.init();
}
break;
case 'd':
{
system("clear");
changeDifficulty();
}
break;
case 'q':
{
system("clear");
return 0; // return menu(); in main()
}
default:
{
system("clear");
cout << nInvalid option! Press a key to choose: p, u, s, or q\n\n";
break;
}
}
}
}
getChoice Function
char getChoice() {
cout << " Main Menu\n";
cout << "---------------------\n";
cout << "p - play game" << endl;
cout << "u - change user" << endl;
cout << "d - change difficulty" << endl;
cout << "q - quit\n" << endl;
char choice = getchar();
return choice;
}
Everything works fine after it goes through once and runs the code from the default option. Here's what I get every time the program re-enters this loop:
Invalid option! Press a key to choose: p, u, s, or q
Main Menu
---------------------
p - play game
u - change user
d - change difficulty
q - quit
Thanks for any help in advance!
Assuming you use the standard getchar function.
The problem is most likely that when you enter your "choice" you enter the 'p' (for example) and then press the Enter key. That Enter key will also be in the input as a newline '\n'. So the next time you call getChoice (and it calls getchar) you read that newline.
There are basically four ways to solve it:
The C way, using scanf instead of getchar, and ask scanf to read and discard leading whitespace (like newlines):
char choice;
scanf(" %c", &choice);
// Note space in front of the %c, which tells scanf to discard leading whitespace
Read the character, and then read and discard everything else until the newline.
Read the whole line into a string, and the parse out the character.
Use std::cin and the normal formatted input operator >>, as that will skip leading whitespace.
I really recommend the last (number 4) method.
Related
I have written a program with several menus within it. I am now debugging the program and I wanted to include some input validation in the menu choices. However, for some reason, when it detects a wrong input it goes back to the beginning of the function with a goto statement (I know, bad practice :\) and It asks the user for a new input, but even if the input is right, it goes back to the case for non allowed inputs (default) no matter what. Does anyone have any idea of what's going on?
NOTE:
select_variable_check(vector<int> , int) is a function that checks if the value entered has been entered before if that is of any relevance, although I don't think it has anything to do with it.
void select(vector<int>&select_control) {
char select;
choices:
cin >> select;
int selectint = select;
bool check = select_variable_check(select_control, selectint);
switch (select) {
case ('1','2','3','4','5','6','7','8','9','10'):
if (check == false) {
string city = city_selection(selectint);
double xcoor = xcoor_selection(selectint);
double ycoor = ycoor_selection(selectint);
cout << "\n" << city << "\n";
select_control.push_back(selectint);
cout << "\n Enter next city: ";
cin >> select;
selectint = select;
}
else {
cout << "You have already selected that city, please select another one ";
cin >> select;
}
break;
case '99': {
cout << "TERMINATING" << endl;
Sleep(3000);
exit(0);
break;
}
case '100': {
cout << "input complete" << endl;
break;
}
default: {
cout << "not a valid value, please try again" << endl;
goto choices;
break;
}
}
The value of ('1','2','3','4','5','6','7','8','9','10') is '10', so that's the only value that will trigger that first case statement. The right way to write this is:
case '1':
case '2':
case '3':
...
Even with this change, though, '10' is a peculiar kind of character, and almost certainly not the right thing here.
Your code boils down to
start:
get_input
process_input
if good do something
else go to start
end:
Now when you enter bad input it goes back to start. Your input operation will fail again as the input stream is still in an error state so you do not get new input and since you have bad input you go back to start. To stop this loop you need to clear out the error flags on the stream and remove any input still in the buffer. That will make you default case look like
default: {
cout << "not a valid value, please try again" << endl;
cin.clear(); // removes error flags
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // flushes input buffer
goto choices;
break;
}
You will need to #include <limits> to use cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n')
I've only started coding C. (freshman here, no experience, only basic html)
For some reason, whenever I choose the option 'N', it just creates a new line.
At my second scanf whenever I change my '%s', the result changes.
When I use, "%d",after entering a number, it continuously enter "Choose a number between 1-4". If I use "%c",after entering a number, it will skip directly to the loop. Help?
#include <stdio.h>
int main()
{
int n, ok = 0;
char input;
while (ok == 0)
{
printf("Choose a number between 1-4\n");
scanf("%d", &n);
switch (n)
{
case 1:
printf("You've chosen number 1");
break;
case 2:
printf("You've chosen number 2");
break;
case 3:
printf("You've chosen number 3");
break;
case 4:
printf("You've chosen number 4");
break;
default:
printf("You have chosen an invalid number");
}
printf("\nInput again? (Y/N)\n");
scanf("%s", &input);
if (input=='n'||input=='N')
{ok++;}
else if (input=='Y'||input=='y')
{printf("\n");}
}
getchar();
getchar();
}
change
scanf("%s", &input);
to
scanf(" %c", &input);
and get rid of getchar()s at the end. It will work as you wanted. %c specifier reads a char character, but it does not omit whitespace characters. Putting space onto scanf's format will deal with whitespaces and read first non-whitespace char. To be more clear, see what man page says about %c format specifier:
(...) The usual skip of leading white space is suppressed. To skip
white space first, use an explicit space in the format.
However, if you are really learning C++, stick with cin/cout, rather than scanf/printf's.
Here's how your program would look like if you would replace printf/scanf with cin/cout. If you've done that previously, you wouldn't had that kind of trouble.
#include <iostream>
using namespace std;
int main()
{
int n, ok = 0;
char input;
while (!ok)
{
cout << "Choose a number between 1-4\n";
cin >> n;
switch (n)
{
case 1:
cout << "You've chosen number 1";
break;
case 2:
cout << "You've chosen number 2";
break;
case 3:
cout << "You've chosen number 3";
break;
case 4:
cout << "You've chosen number 4";
break;
default:
cout << "You have chosen an invalid number";
}
cout << "\nInput again? (Y/N)\n";
cin >> input;
if (input=='n' || input=='N')
{
ok++;
}
else if (input=='Y' || input=='y')
{
cout << "\n";
}
}
return 0;
}
After this question I figured out that using char as input will avoid the infinite loop caused by typing characters for input which is using int. But now I had met another problem.
When I typed the code below:
#include <iostream>
void language();
int main() {
language();
}
void language() {
char choice;
// Ask user for something and input
std::cout << "Press 1 to exit the program\n\n";
std::cin >> choice;
// Getting user's input and run the code below and find for specific words
switch(choice) {
case '1':
std::cout << "\n\nEnding program...";
return 0;
break;
default:
std::cout << "\n\nPlease type the specific number.\n\n";
language();
break;
}
}
When I compile it I didn't get any errors or warnings. But when I type 12 or similar word with 1 at first, the program will be ended.
And before answering me, I still learning C++. (By the way I don't think I really need to say this?) And because this I didn't know how to solve this. What's happening to my code?
As you want a char from the input, std::cin will just get the first character you type in the input and assign to choice. It will ignore the following characters.
That is, you will enter in the first case of your switch/case condition and return.
It depends on what are the inputs you expect from the user. If you expect only numbers, I suggest you to use an int :
#include <limits> // needed for std::numeric_limits
void language() {
int choice;
// ^^^^
// Ask user for something and input
std::cout << "Press 1 to exit the program\n\n";
std::cin >> choice;
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
// Getting user's input and run the code below and find for specific words
switch(choice) {
case 1:
std::cout << "\n\nEnding program...";
break;
default:
std::cout << "\n\nPlease type the specific number.\n\n";
language();
break;
}
}
Working live example.
Your code is exiting when you enter input starting with character 1.
You want the choice as string, not char since char only contains one character. Also you don't need break after the return statement
char choice;
will just receive a single character from standard input.
so all digits starting with 1 will end your program
instead use int choice; and change case to case 1:
#include<limits>
//...
int choice;
//...
std::cout << "Press 1 to exit the program\n\n";
while(true)
{
if (std::cin >> choice)
break ;
else {
std::cout<<"Not an integer !"<<std::endl;
std::cin.clear() ;
std::cin.ignore(std::numeric_limits<std::streamsize>::max(),
'\n') ;
}
}
switch(choice) {
case 1:
//...
break;
default:
std::cout << "\n\nPlease type the specific number.\n\n";
language();
break;
}
So I'm having some problems when using cin and switch statements.
First, the program prompts for an int:
cout << "How many registers would you like to use?: ";
cin >> kassor;
Later on, the user is asked to make a choice:
cout << endl << "Press \"S\" to run the queue simulator and/or make a timestep." << endl;
cout << "Press \"Q\" to quit." << endl << endl;
cout << "Choice: ";
cin >> choice;
switch (choice)
{
case 's':
case 'S':
{
cout << "test";
nya = newCustomers();
break;
}
case 'q':
case 'Q':
{
return 0;
}
}
Here, the 'q' option works fine, but the 's' option does not. It 'hangs', as if still waiting for input. I have tried various cin.ignore() and such, but to no avail.
What puzzles me is that
switch (choice)
{
case 's':
case 'S':
{
cout << "test";
nya = newCustomers();
break;
Gives nothing, but the following:
switch (choice)
{
case 's':
case 'S':
{
cout << "test";
cin.ignore(1024, '\n');
nya = newCustomers();
break;
outputs 'test'.
My main question here is: Is the problem cin or something in the case: s ?
Thank's in advance :
It looks like the function newCustomers is getting hung up on a stray character or two after the input to choice. That would explain why the ignore call "fixes" the problem, and it's the solution suggested in the comment by #TheBuzzSaw.
To see this more clearly, change
cout << "test";
to
cout << "test\n";
or to
cout << "test" << std::endl;
On some systems the console is line-buffered, so you won't see any output until the program writes a newline. Inserting endl flushes the output buffer, so you should see the message even if subsequent code hangs. Or change it to:
cerr << "test\n";
cerr is more aggressive about flushing buffers, precisely because it's used for error output that you don't want to miss.
Short Answer:
I believe cin is failing somewhere or it is the cause of unexpected behavior
I cannot pinpoint it without more code.
Edit: I cannot post a comment on your post, so I figured I would post here. The "hanging" could be an infinite loop. If you are doing something like what I am doing and looping to get input and fail to clear the error bits cin will constantly fail. If you are not cout'ing something each time you ask for input it could just be quietly sitting there looping through that input-gathering function. Put breakpoints in all of your loops and step through with the debugger to verify none of your loops are infinite looping.
Try adding a std::cin.clear(); before the .ignore() in your second test case. See if that stops your hanging.
Explanation:
cin can fail. This can cause weird behavior. A common way for it to fail is if you are reading into an integer and you get character input. Once cin fails it sets a fail bit and from then on, cin does not behave like you would expect.
I would recommend not using a bare cin >> choice, because it can fail and you wont know it. I would abstract this out to a method that gets the proper input you want.
I personally keep a tiny utility library(.h) and .cpp around to include in projects where I am using common functionality I have already coded.
I have a readIntBetween() function which accepts 2 integers and reads from standard input an integer between those two numbers. If the user does not provide the right input (integer over or under bounds, or input containing a character) I ask them to re-enter the input.
In this function I make sure to clear the fail bits when I have detected a failure, and I ignore something like 200 characters to "flush" it out.
Here is the code to my readIntBetween function. I hope it helps you diagnose your error and fix it:
int readIntBetween(int lower, int upper, std::string prompt)
{
bool goodVal = false;
int value = -1;
do
{
std::cout << prompt ;
std::cin >> value;
if ( value >= lower && value <= upper)//check to make sure the value is in between upper and lower
goodVal = true;
else
{
if(std::cin.fail())
{
std::cout << "\tError - invalid format for integer number" << std::endl;
//clear all status bit including fail bit
std::cin.clear();
//flush input buffer to discard invalid input
std::cin.ignore(1000, '\n');
}
else
std::cout << "Value is not valid, must be between " << lower<<" and "<<upper<<"."<<std::endl;
}
}while(!goodVal);
return value;
}
If your cin is being used inside a loop, I recommend a different approach.
char buffer[64];
cin.getline(buffer, 64);
char choice = buffer[0];
switch (choice)
{
...
}
It is possible your cin is not being reset, and any extraneous characters are being fed into the subsequent requests for input. Grab more input and only process its first character.
Here's the code :
cout << "Please enter the file path: ";
string sPath;
getline(cin, sPath);
cout << "Please enter the password: ";
string sPassword; getline(cin, sPassword);
Problem is, when I run it it displays "Please enter the file path: " then it displays "Please enter the password: " and then waits for the password. It seems to completely skip the first 'getline()'.
Later edit: Yes there are some input operations done before.
int iOption = 0;
while (iOption == 0)
{
cout << "(E/D): ";
switch (GetCH())
{
case 'E':
iOption = 1;
break;
case 'e':
iOption = 1;
break;
case 'D':
iOption = 2;
break;
case 'd':
iOption = 3;
break;
default:
break;
}
}
And the code for GetCH() in case anyone asks.
char GetCH ()
{
char c;
cin >> c;
return c;
};
It looks like the rest of the line that was input for GetCH still remains in the buffer at the time that you call getline, i.e. at least a \n and this is what you are reading in the first getline call. The program doesn't block waiting for user input because the getline request can be satisfied by the partial line still queued for reading.
Consider modifying your GetCH function to read whole lines as well.
E.g. something like (totally untested, I'm afraid):
int GetCH()
{
std::string inputline;
// Read until error or we receive a non-empty line
while( std::getline(std::cin, inputline) && inputline.empty() )
{
}
return inputline.empty() ? EOF : inputline[0];
}
You need to clear whatever available in input stream like below
cin.clear();
cin.ignore(std::numeric_limits<std::streamsize>::max())
I have a cin.clear() before the while loop and have modified the GetCH option to get a whole string with 'getline' and only return the first letter.
char GetCH ()
{
string c;
getline(cin, c);
return c[0];
};
It works like a charm now. Thanks to everyone for the help.