C 当预期值为int类型而输入的值不是int类型时,如何验证用户输入?
我有以下代码:C 当预期值为int类型而输入的值不是int类型时,如何验证用户输入?,c,error-handling,C,Error Handling,我有以下代码: #include <stdio.h> #define MIN 0 #define MAX 9 int main() { int n; while (1) { printf("Enter a number (%d-%d) :", MIN, MAX); scanf("%d", &n); if (n >= MIN && n <= MAX) {
#include <stdio.h>
#define MIN 0
#define MAX 9
int main()
{
int n;
while (1) {
printf("Enter a number (%d-%d) :", MIN, MAX);
scanf("%d", &n);
if (n >= MIN && n <= MAX) {
printf("Good\n");
} else {
printf("Damn you!\n");
break;
}
}
return 0;
}
但是,当用户输入任何意外输入(如
-即^[[A
,或任何字符串,如abc
或abc def
等)时,它将失败并进入无限循环
$ ./a.out
Enter a number (0-9) :2
Good
Enter a number (0-9) :^[[A
Good
Enter a number (0-9) :Good
Enter a number (0-9) :Good
Enter a number (0-9) :Good
Enter a number (0-9) :Good
Enter a number (0-9) :Good
Enter a number (0-9) :Good
^C
需要注意的一点是:当用户第一次输入
时,它会按预期工作!例如
$ ./a.out
Enter a number (0-9) :15
Damn you!
$ ./a.out
Enter a number (0-9) :5
Good
Enter a number (0-9) :3
Good
Enter a number (0-9) :-1
Damn you!
$ ./a.out
$ ./a.out
Enter a number (0-9) :^[[A
Damn you!
$
为什么会出现这种奇怪的行为?我们应该如何处理用户输入不合适的内容的情况?我的建议是检查scanf()的返回值。如果它为零,则表示匹配失败(即用户没有输入整数) 它之所以成功,是因为当匹配失败时,scanf()不会改变n,所以检查是在未初始化的“n”上执行的。我的建议是,总是初始化所有内容,这样你就不会得到像那样奇怪的逻辑结果 例如:
if (scanf("%d",&n) != 1))
{
fprintf(stderr,"Input not recognised as an integer, please try again.");
// anything else you want to do when it fails goes here
}
我会使用
char
缓冲区来获取输入,然后使用atoi
将其转换为整数。
这里唯一的问题是atoi
在失败时返回0(您无法确定是因为失败还是因为值为0)
您还可以将字符串与strncmp
进行比较
//编辑:
根据注释中的建议,您可以使用isdigit()
由于我有点赶时间,所以无法在您的用例中实现我的示例,但我也怀疑这是否会导致任何问题
一些示例代码是:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
int main(void)
{
int x;
char buf[4] = {0};
scanf("%s",buf);
if(isdigit(buf[0]))
{
x = atoi(buf);
if( x > 9)
{
// Not okay
}
else
{
// okay
}
}
else
{
// Not okay
}
return 0;
}
#包括
#包括
#包括
内部主(空)
{
int x;
char buf[4]={0};
scanf(“%s”,buf);
if(isdigit(buf[0]))
{
x=原子指数(buf);
如果(x>9)
{
//不好
}
其他的
{
//好的
}
}
其他的
{
//不好
}
返回0;
}
如果缓冲区的第一个元素不是数字,您就知道它的输入是错误的
否则,您现在用
atoi
检查值,看看它是否大于9。(您不需要检查较低的值,因为在isdigt
调用中已经检测到-1(buf[0]
将为“-”)我已经按如下方式更新了代码(选中scanf()
返回值),并且它工作正常
#include <stdio.h>
#include <errno.h>
#define MIN 0
#define MAX 9
int main()
{
int n, i;
while (1) {
errno = 0;
printf("Enter a number (%d-%d) :", MIN, MAX);
if (scanf("%d", &n) != 1) {
printf("Damn you!\n");
break;
}
if (n >= MIN && n <= MAX) {
printf("Good\n");
} else {
printf("Damn you!\n");
break;
}
}
return 0;
}
#包括
#包括
#定义最小值0
#定义最大值9
int main()
{
int n,i;
而(1){
errno=0;
printf(“输入一个数字(%d-%d):”,最小值,最大值);
如果(scanf(“%d”,&n)!=1){
printf(“该死的!\n”);
打破
}
如果(n>=MIN&&nscanf返回它读取的字段数,那么您可以执行以下操作
if(scanf(“%d”,&n)我个人建议,对于交互式用户输入,尤其是数字输入,完全放弃scanf
。它不够健壮,无法处理某些不好的情况
%d
转换说明符告诉scanf
读取下一个非数字字符(忽略任何前导空格)。假设调用
scanf("%d", &val);
如果输入流看起来像{'\n','\t',''1','2','3','\n'},scanf
将跳过前导空格字符,读取并转换“123”,并在尾随的换行符处停止。值123
将分配给val
,而scanf
将返回值1,表示成功分配的数量
如果输入流看起来像{'a','b','c','\n'},scanf
将在a
处停止读取,不向val
分配任何内容,并返回0(表示未成功分配)
到目前为止,还不错,对吧?好吧,这里有一个丑陋的例子:假设您的用户键入“12w4”。您可能希望将整个输入视为无效而拒绝。不幸的是,scanf
将愉快地转换并分配“12”,而保留“w4”在输入流中,阻塞下一次读取。它将返回1,表示分配成功
这是另一个丑陋的例子:假设您的用户键入了一个令人讨厌的长数字,如“12345678900123456789012345678901234567890”。同样,您可能希望完全拒绝此输入,但无论目标数据类型是否能代表该值,scanf
都将继续转换并分配它
要正确处理这些情况,您需要使用不同的工具。更好的选择是使用fgets
(防止缓冲区溢出)将输入读取为文本,并使用strtol
手动转换字符串。优点:您可以检测并拒绝像“12w4”这样的坏字符串,您可以拒绝显然太长且超出范围的输入,并且不会在输入流中留下任何垃圾。缺点:这需要更多的工作
下面是一个例子:
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
...
#define DIGITS ... // maximum number of digits for your target data type;
// for example, a signed 16-bit integer has up to 5 digits.
#define BUFSIZ (DIGITS)+3 // Account for sign character, newline, and 0 terminator
...
char input[BUFSIZ];
if (!fgets(input, sizeof input, stdin))
{
// read error on input - panic
exit(-1);
}
else
{
/**
* Make sure the user didn't enter a string longer than the buffer
* was sized to hold by looking for a newline character. If a newline
* isn't present, we reject the input and read from the stream until
* we see a newline or get an error.
*/
if (!strchr(input, '\n'))
{
// input too long
while (fgets(input, sizeof input, stdin) && !strchr(input, '\n'))
;
}
else
{
char *chk;
int tmp = (int) strtol(input, &chk, 10);
/**
* chk points to the first character not converted. If
* it's whitespace or 0, then the input string was a valid
* integer
*/
if (isspace(*chk) || *chk == 0)
val = tmp;
else
printf("%s is not a valid integer input\n", input);
}
}
#包括
#包括
#包括
...
#定义位数…//目标数据类型的最大位数;
//例如,有符号16位整数最多有5位数字。
#定义BUFSIZ(数字)+3//符号字符、换行符和0终止符的帐户
...
字符输入[BUFSIZ];
如果(!fgets(输入,输入大小,标准输入))
{
//输入时读取错误-死机
出口(-1);
}
其他的
{
/**
*确保用户输入的字符串长度不超过缓冲区
*通过查找换行符调整大小以容纳。如果换行符
*如果不存在,则拒绝输入并从流中读取,直到
*我们看到一个换行符或得到一个错误。
*/
如果(!strhr(输入,'\n'))
{
//输入太长
而(fgets(input,sizeof input,stdin)和&!strchr(input,'\n'))
;
}
其他的
{
char*chk;
inttmp=(int)strtol(input,&chk,10);
/**
*chk指向未转换的第一个字符。如果
*如果是空白或0,则输入字符串是有效的
*整数
*/
如果(i空间(*
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
...
#define DIGITS ... // maximum number of digits for your target data type;
// for example, a signed 16-bit integer has up to 5 digits.
#define BUFSIZ (DIGITS)+3 // Account for sign character, newline, and 0 terminator
...
char input[BUFSIZ];
if (!fgets(input, sizeof input, stdin))
{
// read error on input - panic
exit(-1);
}
else
{
/**
* Make sure the user didn't enter a string longer than the buffer
* was sized to hold by looking for a newline character. If a newline
* isn't present, we reject the input and read from the stream until
* we see a newline or get an error.
*/
if (!strchr(input, '\n'))
{
// input too long
while (fgets(input, sizeof input, stdin) && !strchr(input, '\n'))
;
}
else
{
char *chk;
int tmp = (int) strtol(input, &chk, 10);
/**
* chk points to the first character not converted. If
* it's whitespace or 0, then the input string was a valid
* integer
*/
if (isspace(*chk) || *chk == 0)
val = tmp;
else
printf("%s is not a valid integer input\n", input);
}
}