Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scanf not working in while loop when nonmatching string is entered

Tags:

c

scanf

I'm using a function called checkType to check whether the user has entered a valid input of integer type. For example, if the user enters 15 it will print valid and 15c will print not valid. However, if the user enters a string input only like ccccc, it results in an infinite loop and the program crashes. I have added some screenshots below to show the outputs.

int checkType(int input, char *c) {
  if (input == 2 and *c == '\n') {
    return 1;
  } else {
    return 0;
  };
}

int main(void) {
  int faces = 0;
  char c;
  int valid = 0;
  int input;

  while (valid == 0) {
    printf("Enter number of faces: ");
    input = scanf("%d%c", &faces, &c);
    valid = checkType(input, &c);
    printf(valid == 0 ? "not valid\n" : "valid\n");
  }
}

enter image description here

Infinite Loop:

enter image description here

like image 465
William Avatar asked Nov 17 '20 07:11

William


People also ask

Why can't I use scanf in a while loop?

The reason is scanf reads directly from the standard input and which blocks and waits for user input after it has processed the line. What you need to do is read the line and process that line in your while loop. I've modified your code below.

What's wrong with scanf ()?

The problem with scanf () is that when the conversion fails it will not consume the content of the input buffer... For example... ? ... will go into an infinite loop if you type the letter A and hit enter. Frankly I see this as a terrible flaw in C's input functions, they should ALWAYS wait, no matter what... others will have varying opinions.

Why use scanf () instead of getchar () in loops?

If the user enters several letters, the getchar () will remove only one char, per loop, but the program will not crash. So when I see scanf ()'s used in a loop, without such guards in place, and the program is looping until it crashes - it's the first thing that crosses my mind.

Why does scanf () leave a newline in the input stream?

If you use scanf (), it will always leave a newline char behind, in the input stream. Scanf () can ignore it, if the input is the wrong size, but sometimes it will goof up, and accept the newline, as your entry. Especially if the scanf () is for a type char - then it will always seem to "skip" the entry, and go on. ?


3 Answers

The scanf() family of functions is not really made for input of questionable syntax.

The usual approach to solve your problem is to read in all the input in a way which is sure to succeed, e.g. by reading a complete line (instead of a number, word-like string, or anything else with expected format) with fgets(). Then you can try to parse that line-representing string as by expected format. (You can use sscanf() for the parsing attempt.) If that fails you ignore it and read the next line, or try parsing according to an alternative allowed format for the same input.

The relevant difference is that reading a whole line will succeed and remove it from the input stream. In contrast to that, your code, in case of syntax failure, leaves in the input stream whatever did not match the expected syntax. As such it will, of course, fail the next iteration of the reading loop again. That is what causes your endless loop.

In detail:

  • read a whole line into a buffer,
    using fgets() and the option to restrict the number of characters to the size of the buffer
  • at this point all of the line is removed from the input (aka stdin, aka input stream),
    which means that the next read will get new input even if the read line does not match any allowed format,
    this is the relevant difference to trying to read input directly with scanf()
  • from the line buffer, attempt to read separate values according to an allowed format,
    using sscanf() and with a check of the return value
  • if successful great, you have your expected variables filled (e.g. an integer);
    you could try scanning for additional, not format-covered trailing input (and continue like for incorrect input, even if the beginning of the line matched an allowed format)
  • if not successful try a different allowed format,
    using sscanf() again,
    from the same line buffer, which is not changed, not even if a format partially matched
  • if no allowed format matches the input it is time to consider it incorrect
  • incorrect input can be ignored or can cause a fatal parsing error in your program, your choice
like image 76
Yunnosch Avatar answered Nov 15 '22 03:11

Yunnosch


However, if the user enters a string input only like ccccc,
it results in an infinite loop and the program crashes

Reason : scanf returns the number of valid read values from stdin. If we give cccc as input, scanf cannot read the value, because the input and variable faces are of different data type. This makes input = 0. So the function checkType returns 0, which in turn makes valid = 0. This makes while(valid == 0) always true and hence the endless loop.

like image 27
Krishna Kanth Yenumula Avatar answered Nov 15 '22 04:11

Krishna Kanth Yenumula


Scanf will read form stdin, and not clean the stdin buffer if not match. So it will read wrong data again in your case, you can just add a clean function with stdin after scanf, like: __fpurge(stdin)

You can refer to the following two links:

https://man7.org/linux/man-pages/man3/scanf.3.html

http://c-faq.com/stdio/stdinflush2.html

like image 29
DDKV587 Avatar answered Nov 15 '22 02:11

DDKV587