【问题标题】:How do you deal with false input?如何处理错误输入?
【发布时间】:2021-02-25 18:01:22
【问题描述】:

我想创建一个将整数作为变量的程序。我想要一种方法来处理在缓冲区上扫描字符串或字符时引起的错误。我的代码看起来像这样:

int input;

printf("Enter an integer: ");
scanf("%d", &input);

就这么简单,但我希望程序在有人将“test”输入缓冲区时打印出“请输入一个数字”之类的内容,同时仍在使用 scanf

【问题讨论】:

  • 您使用fgets()(和strtol())作为用户输入...并从您的工具箱中删除scanf()
  • 看看这个处理错误处理的答案:stackoverflow.com/a/5087087
  • 如果你想继续使用scanf,至少你可以在这里测试一下这个函数的返回值等于1。
  • 无论出于何种意图和目的,使用scanf 都无法解决此问题。如果你想编写一个在错误输入时表现优雅的程序(这是一个好的和崇高的目标!),实现它的唯一明智的方法是使用scanf之外的技术other。请参阅here 作为开始。 scanf 根本无法很好地处理错误的输入,并且尝试添加代码以使其很好地处理会遇到越来越多的困难,直到从其他东西开始会容易得多。
  • 为什么要使用scanf?是因为您的老师告诉您必须这样做,还是因为您认为这是在 C 中进行用户输入的正确方法?如果你的老师坚持,我帮不了你,否则,请听从你大概来SO找的专家建议,并了解scanf基本上是可怕。要做任何花哨的事情,使用scanf 以外的其他东西比使用scanf 容易得多。而且,不幸的是,就scanf 而言,“在用户输入错误时提示用户”绝对算作“花哨的东西”。

标签: c error-handling scanf


【解决方案1】:

关于scanf%d 转换说明符需要记住两点:

  1. %d 告诉scanf 跳过任何前导空格,然后读取任何十进制数字字符,直到第一个非十进制数字字符;

  2. scanf 返回成功转换和分配的项目数。

所以,下面是 scanf( "%d", &input ) 根据不同输入的行为:

Input text        Value saved to input        Return value
----------        --------------------        ------------
   "  abc"                         n/a                   0
   "  12c"                          12                   1
   "  1b3"                           1                   1
   "  123"                         123                   1

在所有情况下,%d 转换说明符告诉scanf 跳过前导空格。

如果输入的第一个非空白字符是非数字字符,scanf 将在该点停止读取,input 不会更新,它会返回 0

如果第一个非空白字符十进制数字,scanf 将读取到第一个非十进制数字字符,然后将生成的数字字符串转换并分配给input。不幸的是,这并不能帮助您捕获"12c""1b3" 之类的情况——只要字符串中至少有一个十进制数字,scanf( "%d", ... ) 就会将其视为成功并返回1

所以,如果你想验证你的用户是否输入了一个有效的十进制整数字符串,你必须做两件事:

  1. 你要检查scanf的返回值;
  2. 您必须检查输入结尾后的第一个字符,以查看它是否为空格。

有几种方法可以完成第二部分。您可以在十进制整数输入之后立即读取字符 - 如果它不是空格,则输入无效:

char dummy;
int itemsRead = scanf( "%d%c", &input, &dummy );

if ( itemsRead == 0 || itemsRead == 2 && !isspace( dummy ) )
{
  fprintf( stderr, "Input is not valid, try again!\n" );
  while ( getchar() != '\n' )
    ; // empty loop
}

如果itemsRead0,那么第一个输入的字符不是十进制数字字符,输入不好。

如果itemsRead2并且dummy不是空格字符,那么我们就有"12c""1b3"这样的情况,输入不好。

如果输入不正确,则写入错误消息并使用getchar 清除任何剩余字符的输入流直到下一个换行符。

如果itemsRead1,那么我们看到的只有十进制数字字符后的EOF,所以输入是好的,没有其他事情可做。如果itemsRead2 并且dummy 是空格,那么输入是好的并且没有其他事情可做(我假设您不需要将空格推回输入流)。

或者,您可以将输入读取为文本并使用strtol 将其转换为整数。 strtol 还会设置一个指针,指向第一个转换的字符:

#define MAX_INPUT_SIZE 11 // A 32-bit integer takes up to 10 decimal digits,
                          // plus a possible sign character. 

char buffer[MAX_INPUT_SIZE + 1];  // +1 for string terminator

if ( fgets( buffer, sizeof buffer, stdin ) )
{
  char *chk;
  int tmp = (int) strtol( buffer, &chk, 10 ); // convert decimal digit string
                                              // to an integer value
  if ( isspace( *chk ) || *chk == 0 )
  {
    input = tmp;
  }
  else
  {
    fprintf( stderr, "%s is not a valid input, try again\n", buffer );
  }
}
else
{
  // error on input
}
   

您会注意到我将 strtol 的结果分配给了一个临时的 - 在您验证输入正确之前不要更新您的目标是个好主意。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-19
    • 1970-01-01
    • 1970-01-01
    • 2021-12-15
    相关资源
    最近更新 更多