3

I'm having a problem with multiple characters while using a while loop. I'm writing a code that would direct the user to a new function based on the input of either "y" or "n". When I scanf for one character it works fine; however, when the user types in multiple characters the while loop repeats.

#include <stdio.h>
int main()
{
    char x;
    printf("type in letter n or y\n");
    scanf("%c", &x);
    while (x!= 'Y' && x!='N' && x!= 'n' && x!='y')
    {
        printf("Invalid, please type Y/N to continue: \n");
        scanf(" %c", &x);
    }
    if (x== 'Y' || x == 'y')
    {
        printf("y works");
    }
    if (x =='N' || x =='n')
    {
        printf("n works");     
    }
}

For example, if I type in hoyp, it would say "Invalid, ..." 2 times and then the "y works" would be written on the third line. How can the code be changed so that the invalid would only be said once, and the user must input again to allow the program to continue?

Jonathan Leffler
  • 666,971
  • 126
  • 813
  • 1,185
onutes
  • 33
  • 4
  • Does this answer your question? [Good way to flush scanf buffer when invalid entry entered](https://stackoverflow.com/questions/24073138/good-way-to-flush-scanf-buffer-when-invalid-entry-entered) – moooeeeep May 19 '21 at 10:07

2 Answers2

3

This is how scanf behaves. It keeps reading in all the characters you've entered. You can accept a string as input first using fgets and extract and check only its first character. fgets allows you to specify the exact number of characters to be read. I have first declared a char array of size 4096. This will work when the input is up to 4095 characters. You can adjust the size as per your needs.

#include <stdio.h>
int main()
{   
    char x, buffer[4096];   
    printf("type in letter n or y\n");
    fgets(buffer, 4096, stdin);
     x = buffer[0];
     while (x!= 'Y' && x!='N' && x!= 'n' && x!='y')            
     {        
         printf("Invalid, please type Y/N to continue: \n");         
         fgets(buffer, 4096, stdin);
         x = buffer[0];         
     }        
     if (x== 'Y' || x == 'y')          
     {           
         printf("y works");
     }           
     if (x =='N' || x =='n')        
     {           
         printf("n works");           
     }  
 } 
Amal K
  • 2,135
  • 9
  • 27
  • 2
    Your `fgets` still reads only one character due to the buffer size, so this doesn't fix the issue. – interjay May 19 '21 at 09:37
  • 2
    Use `char buffer[4096];` and `if (fgets(buffer, sizeof(buffer), stdin) != NULL)` — you can increase the array size if you think 4096 is too small. You should always check that the input operation succeeded, hence the `if` test. – Jonathan Leffler May 19 '21 at 09:39
  • 1
    @interjay Thanks. I've fixed it – Amal K May 19 '21 at 09:46
  • 1
    thanks,I have solved the problem,this solution works with e.g "gyoh" but doesn't work with "yesno", because it only takes the first charcter, i solved this by adding || string0[1] != '\n') for the while function, and && string0[1] == '\n') for the if functions. Thank you. – onutes May 19 '21 at 10:55
0

Here is my approach to the problem:

  1. I have used fgets() instead of scanf(). See why here.
  2. I have used the suggestion by users jamesdlin and M.M in this question to solve the repeated printing issue when the input is more than one character or if the input is empty. I encourage you to read the whole thread to know more about this issue.
  3. (Optional) Used some extra headers for better code readability in the loop conditions. I think the fgets() could be used in the condition of the while() but I got used to the pattern I have written below.

Edit: added a condition to reject inputs with length > 1. Previously, inputs that starts with 'y' or 'n' will be accepted (and are interpreted as 'y' or 'n' respectively) regardless of their length.


#include <stdio.h>
#include <stdbool.h>
#include <ctype.h>

void clearInput();

int main()
{   
    // allocate space for 'Y' or 'N' + '\n' + the terminator '\0'
    // only single inputs will be accepted
    char _inputbuff[3];
    char choice;
    bool isValidInput = false;

    
    while(!isValidInput) {
        printf("Please enter your input[y/n]: ");
        
        // use fgets() instead of scanf
        // this only stores the first 2 characters of the input
        fgets(_inputbuff, sizeof(_inputbuff), stdin);
        
        // don't accept empty input to prevent hanging input
        if(_inputbuff[0] == '\n') {
            printf("Empty input\n");
            // go back to the top of the loop
            continue;
        }
        
        // input is non-empty
        
        // if the allocated space for the newline does not
        //  contain '\n', reject the input
        if(_inputbuff[1] != '\n') {
            printf("Input is more than one char.\n");
            clearInput();
            continue;
        }
        
        choice = _inputbuff[0];
        // printf("The input is %c\n", choice);
        
        // convert the input to uppercase for a 'cleaner' code
        //  during input validation
        choice = toupper(choice);
        
        // the input is not 'Y' or 'N'
        if(choice != 'Y' && choice != 'N') {
            printf("Please choose from Y or N only.\n");
            
            // go back to the top of the loop
            continue;
        }
        
        // the input is 'Y' or 'N', terminate the loop
        isValidInput = true;
    }
    
    // conditions for 'Y' or 'N'
    
    if(choice == 'Y') {
        printf("The input is Yes.\n");
        return 0;
    }
    
    if(choice == 'N') {
        printf("The input is No.\n");
        return 0;
    }
}  

void clearInput() {
        int _clear;
        // clear input stream to prevent repeated printing of invalid inputs
        while ((_clear = getchar()) != '\n' && _clear != EOF ) { }
}

(This is my first time answering a question and it has been a while since I have used C so feel free to give suggestions/corrections regarding my answer. Thanks!)

appauldev
  • 21
  • 3