仅在C中输入验证整数

仅在C中输入验证整数,c,cs50,C,Cs50,我的节目是马里奥金字塔。我似乎无法在C中完成输入验证。如果有人能解释输入验证和出错的地方。谢谢这是我的密码副本 // Prompt user till input is 0-23 do { printf("Welcome to Mario Pyramid Builder! Enter a number from 0 - 23: "); scanf("%d", &height); if(height >= 0 && height <= 2

我的节目是马里奥金字塔。我似乎无法在C中完成输入验证。如果有人能解释输入验证和出错的地方。谢谢这是我的密码副本

// Prompt user till input is 0-23
do
{
    printf("Welcome to Mario Pyramid Builder! Enter a number from 0 - 23: ");
    scanf("%d", &height);
    if(height >= 0 && height <= 23)
    {
        break;
    }
    else
    {
        printf("Wrong Input! Try Again.");
        continue;
    }
}
while ((height < 0) || (height > 23));
//提示用户直到输入为0-23
做
{
printf(“欢迎使用马里奥金字塔生成器!输入一个0-23:”)之间的数字;
扫描频率(“%d”和高度);
如果(高度>=0和高度23));

scanf
不会删除额外的字符,它只提取您在格式说明符中输入的字符,其他字符(如\n)保留在缓冲区中。为避免scanf复杂化,请使用fgets从键盘读取一行,然后使用
sscanf()
提取整数(或仅使用普通的
atoi()

。。。
字符缓冲区[128];
如果(fgets(buffer,sizeof(buffer,stdin)!=NULL)
{
如果(sscanf(缓冲区、%d、&H)==1)
{

如果(height>=0&&heightC中的输入验证是一件麻烦事,需要您做一些工作。不要依靠
scanf
来为您做艰苦的工作,因为它不会。如果您输入像
12dw
这样的输入,使用
%d
转换说明符的
scanf
将成功读取、转换和分配
12
到您的目标,同时在输入流中保留
dw
,以干扰下一次读取

理想情况下,对于交互式输入,您应该将输入读入文本缓冲区,然后自己转换为目标类型。这将有助于防止出现上述情况,并有助于防止出现令人讨厌的长输入试图利用溢出漏洞或类似情况

因此,从输入缓冲区的固定大小数组
char
开始:

#define MAX_INPUT_LENGTH 13     // enough for 32-bit decimal integer, plus sign
char input[MAX_INPUT_LENGTH+1]; // +1 for string terminator
您将使用
fgets
读取输入:

if ( !fgets( input, sizeof input, stdin ) )
{
  // error on input
}
else
{
  // process input
}
您要进行的下一项检查是查看输入缓冲区中是否有换行符-如果没有,则用户输入的值太大,无法表示为本机整数类型。如果是这种情况,则您希望放弃当前输入,然后读取并放弃直到下一个换行符的所有内容:

char *newline = strchr( input, '\n' );
if ( !newline )
{
  while ( getchar() != '\n' ) // read and discard everything up to the next newline
    ;
}
else
{
  // convert input
}
现在,您可以将输入从文本转换为整数了。使用
strtol
库函数(原型包括
stdlib.h
),因为它允许检查输入中是否有任何非数字、非空白字符

char *chk;
height = (int) strtol( input, &chk, 0 );
if ( !isspace( *chk ) || *chk != 0 ) // include ctype.h for isspace
{
  // conversion was unsuccessful, bad input
}
else
{
  // check range
}
把所有这些放在一起,你会得到如下结果:

#include <stdlib.h>
#include <ctype.h>
...
#define MAX_INPUT_LENGTH 13
...
int height;
int done = 0;

printf( "Welcome to Mario Pyramid Builder!\n" );
do
{
  char input[MAX_INPUT_LENGTH+1];
  height = -1;                        // initialize height to an invalid value
  printf( "Enter a number from 0 to 23: " );
  if ( !fgets( input, sizeof input, stdin ) )
  {
    fprintf( stderr, "Error on input, try again!\n" );
  }
  else
  {
    //
    // make sure input wasn't too long for the input buffer by 
    // checking for a newline.
    //
    char *newline = strchr( input, '\n' );
    if ( !*newline )
    {
      while ( getchar() != '\n' )
        ;
      fprintf( stderr, "Input too long, try again!\n" );
    }
    else
    {
      //
      // Convert text in input buffer to an integer.  chk will point
      // to the first character in input that is not a decimal digit.
      // If that character is not 0 or whitespace, then we have
      // bad (non-numeric) input.
      //
      char *chk;
      height = strtol( input, &chk, 10 );
      if ( !isspace( *chk ) || *chk != 0 )
      {
        fprintf( stderr, "Non-numeric input detected, try again!\n" );
      }
      else
      {
        // 
        // if height is between 0 and 23, done will be set to true,
        // and we'll exit the loop.
        //
        done = height >= 0 && height <= 23;
      }
    }
  }
} while ( !done );
#包括
#包括
...
#定义最大输入长度13
...
内部高度;
int done=0;
printf(“欢迎来到马里奥金字塔生成器!\n”);
做
{
字符输入[最大输入长度+1];
高度=-1;//将高度初始化为无效值
printf(“输入一个从0到23的数字:”);
如果(!fgets(输入,输入大小,标准输入))
{
fprintf(stderr,“输入错误,重试!\n”);
}
其他的
{
//
//确保输入对于输入缓冲区来说不是太长
//正在检查换行符。
//
char*newline=strchr(输入“\n”);
如果(!*换行符)
{
而(getchar()!='\n')
;
fprintf(stderr,“输入太长,请重试!\n”);
}
其他的
{
//
//将输入缓冲区中的文本转换为整数。chk将指向
//输入中非十进制数字的第一个字符。
//如果该字符不是0或空白,则
//输入错误(非数字)。
//
char*chk;
高度=strtol(输入和chk,10);
如果(!isspace(*chk)|*chk!=0)
{
fprintf(stderr,“检测到非数字输入,请重试!\n”);
}
其他的
{
// 
//如果高度介于0和23之间,则“完成”将设置为true,
//我们将退出循环。
//

done=height>=0&&height代替1980年代风格的交互式输入,为什么不使用
argv
scanf
返回成功转换的次数,在您的情况下应该是1。如果返回值不是1,则用户输入垃圾,您需要读取换行符之前的所有字符,然后重试。另外,由于循环将
中断
当输入有效时,您不需要
do/while
。一个简单的
while(1)
就是所需的一切。@tadman CS50…@AnttiHaapala也许他们应该更新它以使用本世纪的约定。还有和朋友们,当你说!=1时,它们比If语句中的
atoi
有更好的错误检查。我能做类似的事情吗?而且当我运行它时,它会拒绝有效信息。!=(height>=0&&height@ConnorHutch我在原始答案中省略了细节,现在我编辑了答案以使其更清晰。回答很好,但您可以重新启动
fgets()
在非整数输入的情况下。@chqrlie oops typo,感谢您指出这一点。关于重新启动,我认为这将在他的while循环中运行,因此,如果错误,可以继续。
#include <stdlib.h>
#include <ctype.h>
...
#define MAX_INPUT_LENGTH 13
...
int height;
int done = 0;

printf( "Welcome to Mario Pyramid Builder!\n" );
do
{
  char input[MAX_INPUT_LENGTH+1];
  height = -1;                        // initialize height to an invalid value
  printf( "Enter a number from 0 to 23: " );
  if ( !fgets( input, sizeof input, stdin ) )
  {
    fprintf( stderr, "Error on input, try again!\n" );
  }
  else
  {
    //
    // make sure input wasn't too long for the input buffer by 
    // checking for a newline.
    //
    char *newline = strchr( input, '\n' );
    if ( !*newline )
    {
      while ( getchar() != '\n' )
        ;
      fprintf( stderr, "Input too long, try again!\n" );
    }
    else
    {
      //
      // Convert text in input buffer to an integer.  chk will point
      // to the first character in input that is not a decimal digit.
      // If that character is not 0 or whitespace, then we have
      // bad (non-numeric) input.
      //
      char *chk;
      height = strtol( input, &chk, 10 );
      if ( !isspace( *chk ) || *chk != 0 )
      {
        fprintf( stderr, "Non-numeric input detected, try again!\n" );
      }
      else
      {
        // 
        // if height is between 0 and 23, done will be set to true,
        // and we'll exit the loop.
        //
        done = height >= 0 && height <= 23;
      }
    }
  }
} while ( !done );