如何验证 scanf 数字输入

How can I validate scanf numeric input

我有一个简单的问题。我正在学习链表,我的数据类型是 int。我想知道如何在不允许接受字母输入的地方验证它。

当我按照我的方式执行时,它不打印消息。那么我怎样才能正确地做这样的事情呢?

我的代码:

node * createLinkedList()
{
    node * head = NULL;
    node * temp = NULL;
    node * p = NULL;
    
    while ((scanf("%d", &(temp->data))) !=EOF )
    {
        if ((temp->data>= 'a' && temp->data<= 'z') || (temp->data>= 'A' && temp->data<= 'Z')){
            fprintf(stderr,"ERROR alph is not allowed");
        }

    }
}

%dscanf说明符是读int,如果输入的是字母字符,scanf会读不出来,temp->data会保留其先前的值。

您的周期更好的条件是:

while (scanf("%d", &(temp->data)) == 0) //while input is not correctly parsed...
{
    fprintf(stderr, "ERROR alph is not allowed\n"); //...print error message...
    int c;
    while((c = getchar()) != '\n' && c != EOF) {} //...and clear stdin
}

这样一来,如果输入无法解析为 int,则会询问新的输入,直到输入合适的值。

请注意,, an input starting with a digit but that contains alphabetic characters will be parsed until the character is found, i.e. 123abc4 will parse 123. If you want to avoid this, using a combination of fgets and strtoll 是一种更可靠的方法,可以让您像上面那样验证输入。

如果您希望允许这种情况发生,并继续使用 scanf,请不要忘记您需要清除 stdin,其中将包含未解析的字符,(在示例案例 abc4\n) 中,在要求新输入之前, 完美地涵盖了这些问题,检查一下。


您显示的代码还有一个问题,您的 temp 指针是 NULL,它无法存储任何数据。要么将其声明为对象,要么为其分配内存。实际上,它导致 undefined behavior.


如果您需要验证整数输入,则不能仅依赖 %d。它不会捕获并拒绝像 "12w45" 这样的错误条目 - 它会成功转换和分配 "12" 但会在输入流中留下 "w45" 以扰乱下一次读取。

有两种解决方法。一种是在输入后立即扫描并检查字符,例如:

int tmp;
char dummy = 0;

int r;

if ( (r = scanf( "%d%c", &tmp, &dummy )) == 2 )
{
  // if following character is whitespace, then this is a valid numeric input
  if ( isspace( dummy ) )
    temp->data = tmp;
  else
  {
    fprintf( stderr, "non-numeric character '%c' (%d) detected in input\n",
      isprint( dummy ) ? dummy : '.', dummy );

    fprintf( stderr, "clearing out input stream\n" );
    while ( getchar() != '\n' )
      ; // empty loop
  }
}
else if ( r == 1 ) // only thing following numeric input was EOF
{
  temp->data = tmp;
}
else if ( r == 0 )
{
  fprintf( stderr, "Non-numeric input detected, clearing input stream\n" );
  while ( getchar() != '\n' )
    ; // empty loop
}
else
{
  fprintf( stderr, "EOF or error detected on input\n" );
}

我认为更好的方法是避免完全使用 scanf - 使用 fgets 将输入作为字符串读取,然后使用 strtolstrtod 执行转换:

char buffer[13]; // 11 decimal digits plus sign plus string terminator;
                 // should be able to store the decimal string representation
                 // of any 32-bit integer;

if ( fgets( buffer, sizeof buffer, stdin ) )
{
  // chk will point to the first character *not* converted by strtol
  char *chk;
  int tmp = (int) strtol( buffer, &chk, 10 );
  if ( !isspace( *chk ) && *chk != 0 )
  {
    fprintf( stderr, "Detected non-numeric character '%c' (%d) in input\n",
      isprint( *chk ) ? *chk : '.', *chk );
  }
  else
  {
    temp->data = tmp;
  }
}
else
{
  fprintf( stderr, "EOF or error on input\n" );
}

当你进行这种验证时,使用一个临时文件来存储转换后的值,直到你完成;在您确定该值合适之前,请不要更新您的实际目标。