Validate the type of input in a do-while loop C

2020-01-27 08:14发布

Basically, I need to ensure that input is an integer, like so:

do {
    printf("Enter > ");
    scanf("%d", &integer);
} while (/* user entered a char instead of an int */);

I have tried various methods, but it always end up with run-time error or infinite loop when I tried to enter a char. I have knew that fflush(stdin) is an undefined behavior, which is better not to involve it in my code in order to prevent any error plus it no longer works in VS2015 due to some reasons.

The codes below are the method that I have tried:

typedef enum {false, true} bool;
int ipt;
char c;
bool wrong_ipt;

do {
    c = '\0';
    printf("Enter > ");
    scanf("%d%c", &ipt, &c); //infinite loop occurs while a char has been entered
} while (c != '\n');

do {
    c = '\0';
    printf("Enter > ");
} while (scanf("%d", &ipt) != EOF);

do {
    wrong_ipt = false;
    do {
        ipt = NULL;
        printf("Enter > ");
        scanf("%d", &ipt);
        if (ipt == NULL) {
            wrong_ipt = true;
            break;
        }
    } while (ipt == NULL);
} while (wrong_ipt);

Is there anyway other than fflush(stdin) which can be used to prevent the infinite loop when user entered a char in C?

Thank you

3条回答
Luminary・发光体
2楼-- · 2020-01-27 08:51

The problem is that "scanf()" can leave unread data in your input buffer. Hence the "infinite loop".

Another issue is that you should validate the return value from scanf(). If you expect one integer value ... and scanf returns "0" items read ... then you know something went wrong.

Here is an example:

#include <stdio.h>

void discard_junk () 
{
  char c;
  while((c = getchar()) != '\n' && c != EOF)
    ;
}

int main (int argc, char *argv[])
{
  int integer, i;
  do {
      printf("Enter > ");
      i = scanf("%d", &integer);
      if (i == 1) {
        printf ("Good value: %d\n", integer);
      }
      else {
        printf ("BAD VALUE, i=%i!\n", i);
        discard_junk ();
      }
   } while (i != 1);

  return 0;
}

Sample output:

Enter > A
BAD VALUE, i=0!
Enter > B
BAD VALUE, i=0!
Enter > 1
Good value: 1

'Hope that helps!

查看更多
劫难
3楼-- · 2020-01-27 09:00

Your fundamental mistake is that you never tell your program to consume the invalid input. In words, you told the program:

  • Read an integer if there is one. (Otherwise read nothing)
  • If you didn't get an integer, go back to step 1.

I assume what you thought you were doing was

  • Read the input, and if it was an integer, store it.
  • If it wasn't an integer, go back to step 1.

So what you need to do is to rewrite your code so it does what you meant to do (or come up with some other approach, as in the other answer). That is, write your program to:

  • Read some amount of input. (e.g. a line)
  • Scan the input to see if it is an integer, and store it.
  • If it wasn't an integer, go back to step 1.

Some relevant functions you may find useful are fgets, sscanf, and atoi. (also, try to resist the temptation to write buggy code; e.g. if you intend to read a line of input, make sure you actually do so, and correctly. Many people are lazy and will do the wrong thing if the line is long; e.g. just read part of the line, or cause a buffer overflow)

查看更多
可以哭但决不认输i
4楼-- · 2020-01-27 09:00

The format specifier %d tells to scanf that an integer value is expected in the command line. When the user enters a line of data, it's read as a string, and then scanf attempts to understand if the entered string can be interpreted as the decimal digits of an integer number.

If this interpretation succeds, then the value thus found is stored in the integer variable you passed as argument.

The number of rigth replacements done by scanf are retrieved by that function in the form of an int value. Since you expect only one input, the value 1 will indicates that everything was fine.

So, if there was an error, as for example, the user entered a non-valid integer number, the number returnd by scanf is less than the number of format specifiers. In this case, a value less than 1 informs that an error happened:

 int ipt, succeded;
 do {
    printf("ipt? ");
    succeded = scanf("%d", &ipt);
    if (succeded < 1) {    // Clean the input
      while (getchar() != '\n') 
        ;
    }
 } while(succeded < 1);
查看更多
登录 后发表回答