简体   繁体   中英

In C - scanf() in a for loop is causing printf() to run multiple times

I'm completing an assignment and after completing it, I have 1 bug, and 1 bug fix I made that I don't fully understand. Currently, as long as the user does what is asked, everything works fine. But I know that doesn't happen often, so I'd love to know how to stop these issues. Would love any advice - I am a complete beginner with C.

I found many different pieces of advice here: C: Multiple scanf's, when I enter in a value for one scanf it skips the second scanf

I added a space to my scanf() statements which solved some of the bugs - and I understand that \\n is added onto the end of the entered strings / chars, I'm just not sure how to check for it / handle it, and I tried using getchar() in place of the scanf() but I still get double print / loop problems.

Bug Issue

When the user is running through the game loop, if they enter more than 1 character (for example: 'oo', when prompted with the scanf() to enter 'y' or 'n') my printf statements run 1x per character entered, and connect to each other:

Example would be:

Welcome to Two doors.

Would you like to play? (y/n):Welcome to Two doors.

Would you like to play? (y/n):

This issue also shows up if the user enters 'y' to play the game but then enters a character other than 1,2 or 3 in the second section.

How can I limit the length of their response? Or is the best way to monitor the length of the play and choice variables prior to entering the if statements? Maybe checking to see if they are longer than 1 character and if so, only taking the first character?

Second issue - bug fix that I don't understand In the scanf() functions I ran into a very similar problem to what I described above, but it happened when the user entered any character. The solution I found was to add a space before the character ->

scanf(" %c", &play);

vs

scanf("%c", &play);

Is this issue only a problem when using loops? Since I never found these bugs prior to looping back through the code.

Updated Code with 'while (getchar() != '\\n');' suggestion from Sourav Ghosh

#include <stdio.h>

int main(void) {

    char play;
    int choice;
    char answer[] = "No matter which one you choose the guards both tell you which door leads to death, and therefore you can pick the other door.\n";
    int gameLoop = 1;
    int timesPlayed = 0;

    while (gameLoop == 1){

        if (timesPlayed == 0) {
            printf("Welcome to Two doors.\n");
            printf("Would you like to play? (y/n):");
        } else {
            printf("Would you like to play again? (y/n):");
        }
        scanf(" %c", &play);
        while (getchar() != '\n');

        if (play == 'y') {
            // == instead of =
            printf("\nYou are a prisoner in a room with 2 doors and 2 guards.\n");
            printf("One of the doors will guide you to freedom and behind the other is a hangman --you don't know which is which.\n");
            printf("One of the guards always tells the truth and the other always lies. You don't know which one is the truth-teller or the liar either.\n");
            printf("You have to choose and open one of these doors, but you can only ask a single question to one of the guards.\n");
            printf("What do you ask so you can pick the door to freedom?\n\n");
            printf("\t1.Ask the truth-guard to point to the door of doom.\n");
            printf("\t2.Ask the liar-guard to point to the door of doom.\n");
            printf("\t3.Doesn't matter which one you pick.\n");
            scanf(" %d", &choice);
            while (getchar() != '\n');

            switch (choice) {

                case 1:
                printf("%s", answer);
                timesPlayed++;
                break;

                case 2:
                printf("%s", answer);
                timesPlayed++;
                break;

                case 3:
                printf("%s", answer);
                timesPlayed++;
                break;

                default:
                printf("The Troll Smasher comes out from the shadows and squeezes the stupid out of you until you pop. GAME OVER!\n");
                break;
            }
        } else if(play == 'n') {
            printf("Sorry to hear that, we at Two Doors hope you have a super duper day!\n");
            gameLoop = 0;
            break;
        } else {
            printf("That is not a valid input, please try again by entering either 'y' to start the game or 'n' to quit the game.\n");
        }
    }
    return 0;

}

The problem with %c format specifier is that, it will read only one byte from the input buffer and if the input buffer has more in store and the call in encountered next time, it will not ask for user input , it will simply read the next byte from the available input stream.

So, to answer

How can I limit the length of their response?

well, there's no straightway approach that you can stop the user from entering only X characters/ digits, instead, swipe off the excess, ( if any ) and for the next call, start with an empty buffer is an easy approach.

So, the quick way out of this would be, to clean off the standard input of remaining inputs. You can do something like

  int retval = scanf(" %c", &play);

  //some code

  while (getchar() != '\n');   //eat up the input buffer

  //next call to scanf(), input buffer is empty now....

to stop scanf() from reading already existing unwanted inputs and force it to ask the input from user.

Also, don't forget to check the return value of scanf() to ensure the success of the call.

For the first issue the problem is caused because the execution of the program enters the loop again for example if the user types oo that means that after reading with scanf it is going all the way to the last else. Inside that else none of the variables is modified so when it reenters the loop gameLoop is still 1 and timesPlayed is still 0 so it will print the statements in the first if, then scanf will read the second o and repeat the process. The problem is that scanf reads one character at the time.

Actually for entering one character you can use getchar() but in any case after char input you should clean standard input stream. Consider the following example, that forces the user to the correct input:

    char name[11];
    char answer = 0;
    printf("Would you like to play again? (y/n): ");
    while ((answer = getchar()) != 'y' && answer != 'n')
    {
        printf("You should answer 'y' or 'n'\n");
        // clean the buffer from mess
        while (getchar() != '\n');
    }
    // clean the buffer from mess
    while (getchar() != '\n');
    // next input
    printf("Enter your name: ");
    scanf("%10s", name);
    // clean the buffer from mess
    while (getchar() != '\n');

UPDATE:

Just for clarification, the code

while ((answer = getchar()) != 'y' && answer != 'n')
{
    printf("You should answer 'y' or 'n'\n");
    // clean the buffer from mess
    while (getchar() != '\n');
}

can be be easier to understand while rewritten as

char name[11];
char answer = 0;
printf("Would you like to play again? (y/n): ");
while (1) // infinit loop 
{
    answer = getchar();
    // clean the buffer from mess (immideatly after reading) 
    while (getchar() != '\n');
    if (answer == 'y' || answer == 'n') // check the input
        break; // stop the loop if condition is true
    // or ask again
    printf("You should answer 'y' or 'n'\n");
}
// next input
printf("Enter your name: ");
scanf("%10s", name);
// clean the buffer from mess
while (getchar() != '\n');

in my first example I just optimize the code combining reading and checking the data in parentheses after while : (answer = getchar()) != 'y' is like two actions - answer = getchar() and then answer != 'y'

In the last snippet condition answer != 'y' && answer != 'n' was intentionally replaced with answer == 'y' || answer == 'n' answer == 'y' || answer == 'n' to show difference between "do while data is incorrect" and "stop when correct data get"

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM