3

Given the following code:

#include <stdio.h>
int main()
{
 int testcase;
 char arr[30];
 int f,F,m;
 scanf("%d",&testcase);
 while(testcase--)
 {
  printf("Enter the string\n"); 
  fgets(arr,20,stdin);
  printf("Enter a character\n");
  F=getchar();
  while((f=getchar())!=EOF && f!='\n')
    ;
  putchar(F);
  printf("\n");
  printf("Enter a number\n");
  scanf("%d",&m);
 }
 return 0;
}

I want a user to enter a string, a character and a number until the testcase becomes zero.

My doubts / questions:

1.User is unable to enter a string. It seems fgets is not working. Why?

2.If i use scanf instead of fgets,then getchar is not working properly, i.e whatever character I input in it just putchar as a new line. Why?

Thanks for the help.

TobiMcNamobi
  • 4,687
  • 3
  • 33
  • 52
SanQA
  • 233
  • 3
  • 9
  • 1
    `scanf("%d",&testcase);` --> `scanf("%d%*c", &testcase);` Skip the newline after the number. – BLUEPIXY Jul 20 '17 at 05:09
  • `fgets` reads up from FILE until a new line is encountered. But your very first use of `scanf` is leaving the '\n' character in the input stream, which is then consumed by `fgets` and it thinks the input is done. – WhiteSword Jul 20 '17 at 05:10
  • why do we need to skip the new line after the number? – SanQA Jul 20 '17 at 05:12
  • That newline is received in `fgets`. – BLUEPIXY Jul 20 '17 at 05:13
  • ok thanks a lot@Man – SanQA Jul 20 '17 at 05:14
  • check this, https://stackoverflow.com/questions/29905009/reading-newline-from-previous-input-when-reading-from-keyboard-with-scanf – WhiteSword Jul 20 '17 at 05:15
  • Is it the same with getchar,because when i use scanf instead of fgets,it putchar only a new line? – SanQA Jul 20 '17 at 05:16
  • whatever keystrokes you'll hit on your keyboard will be stored in `stdin`. Therefore, you'll have to consume that extra new line character `'\n'`, because it is there in the input stream. – WhiteSword Jul 20 '17 at 05:26
  • @BLUEPIXY No good if they type a number then press space then press enter – M.M Jul 20 '17 at 05:29
  • Use like [get_int](https://stackoverflow.com/a/45180914/971127) instead of. – BLUEPIXY Jul 20 '17 at 05:44
  • 1
    Better not to mix `scanf()` with `fgets()` to avoid these troubles; try using `fgets()` with `sscanf()` instead of `scanf()` alone. – ad absurdum Jul 20 '17 at 05:45

3 Answers3

5

Mixing functions like fgets(), scanf(), and getchar() is error-prone. The scanf() function usually leaves a \n character behind in the input stream, while fgets() usually does not, meaning that the next call to an I/O function may or may not need to cope with what the previous call has left in the input stream.

A better solution is to use one style of I/O function for all user input. fgets() used in conjunction with sscanf() works well for this. Return values from functions should be checked, and fgets() returns a null pointer in the event of an error; sscanf() returns the number of successful assignments made, which can be used to validate that input is as expected.

Here is a modified version of the posted code. fgets() stores input in a generously allocated buffer; note that this function stores input up to and including the \n character if there is enough room. If the input string is not expected to contain spaces, sscanf() can be used to extract the string, leaving no need to worry about the newline character; similarly, using sscanf() to extract character or numeric input relieves code of the burden of further handling of the \n.

#include <stdio.h>

int main(void)
{
    int testcase;
    char arr[30];
    char F;
    int m;
    char buffer[1000];

    do {
        puts("Enter number of test cases:");
        if (fgets(buffer, sizeof buffer, stdin) == NULL) {
            /* handle error */
        }
    } while (sscanf(buffer, "%d", &testcase) != 1 || testcase < 0);

    while(testcase--)
    {
        puts("Enter the string");
        /* if string should not contain spaces... */
        if (fgets(buffer, sizeof buffer, stdin) == NULL) {
            /* handle error */
        }
        sscanf(buffer, "%29s", arr);
        printf("You entered: %s\n", arr);
        putchar('\n');

        puts("Enter a character");
        if (fgets(buffer, sizeof buffer, stdin) == NULL) {
            /* handle error */
        }
        sscanf(buffer, "%c", &F);
        printf("You entered: %c\n", F);
        putchar('\n');

        do {
            puts("Enter a number");
            if (fgets(buffer, sizeof buffer, stdin) == NULL) {
                /* handle error */
            }
        } while (sscanf(buffer, "%d", &m) != 1);

        printf("You entered: %d\n", m);
        putchar('\n');
    }

    return 0;
}

On the other hand, if the input string may contain spaces, fgets() can read input directly into arr, but then the stored string will contain a \n character, which should probably be removed. One way of doing this is to use the strcspn() function to find the index of the \n:

#include <string.h>  // for strcspn()
/* ... */
        puts("Enter the string");
        /* or, if string may contain spaces */
        if (fgets(arr, sizeof arr, stdin) == NULL) {
            /* handle error */
        }
        /* replace newline */
        arr[strcspn(arr, "\r\n")] = '\0';

        printf("You entered: %s\n", arr);
        putchar('\n');
/* ... */

Note that a maximum width should always be specified when using %s with the scanf() functions to avoid buffer overflow. Here, it is %29s when reading into arr, since arr can hold 30 chars, and space must be reserved for the null terminator (\0). Return values from sscanf() are checked to see if user input is invalid, in which case the input is asked for again. If the number of test cases is less than 0, input must be entered again.

ad absurdum
  • 19,498
  • 5
  • 37
  • 60
1

A simple hack is to write a function to interpret the newline character. Call clear() after each scanf's

void clear (void){
    int c = 0;
    while ((c = getchar()) != '\n' && c != EOF);
}

Refer to this question for further explaination: C: Multiple scanf's, when I enter in a value for one scanf it skips the second scanf

vishalknishad
  • 690
  • 7
  • 12
1

Finally got the solution how can we use scanf and fgets together safely.

#include <stdio.h>
int main()
{
 int testcase,f,F,m;
 char arr[30];
 scanf("%d",&testcase);
 while((f=getchar())!=EOF && f!='\n')
  ;
 while(testcase--)
 {
  printf("Enter the string\n");
  fgets(arr,30,stdin);  
  printf("Enter a character\n");
  F=getchar();
  while((f=getchar())!=EOF && f!='\n')
    ;
  putchar(F);
  printf("\n");
  printf("Enter a number\n");
  scanf("%d",&m);
  while((f=getchar())!=EOF && f!='\n')
  ;
 }
}

We need to make sure that before fgets read anything,flushout the buffer with simple while loop.

Thanks to all for the help.

SanQA
  • 233
  • 3
  • 9
  • This loop style is a good way to clear the input stream. Good that you are checking for both `\n` _and_ `EOF` (this is really mandatory); also good that you are using `int` instead of `char` for `f`, since `EOF` may not fit in a `char`. Also note that this code has no input validation; for example, the user can enter `a` instead of a number for the last input. This would mean no assignment made to `m`, leaving its value indeterminate (possible undefined behavior ahead). – ad absurdum Jul 20 '17 at 07:06
  • You can check the return value from `scanf()` to see if an assignment was made. – ad absurdum Jul 20 '17 at 07:06
  • "how can we use scanf and fgets together safely." + not checking the return value of `scanf()` is not "safe". – chux - Reinstate Monica Jul 20 '17 at 11:49