我有以下代码:
#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;
}
Run Code Online (Sandbox Code Playgroud)
只要用户输入整数值,上述代码就可以正常工作.例如,
$ ./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
Run Code Online (Sandbox Code Playgroud)
但是,当用户进入任何意外的输入(如<up-arrow>-其是^[[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
Run Code Online (Sandbox Code Playgroud)
有一点需要注意:当使用<up-arrow>第一次进入时,它按预期工作!例如,
$ ./a.out
Enter a number (0-9) :^[[A
Damn you!
$
Run Code Online (Sandbox Code Playgroud)
为什么这种奇怪的行为?我们应该如何处理用户输入不合适的内容的情况?
Chr*_*wne 12
我的建议是检查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
}
Run Code Online (Sandbox Code Playgroud)
就个人而言,我建议scanf完全放弃交互式用户输入,尤其是数字输入。它只是不够健壮,无法处理某些不良情况。
该%d转换符告诉scanf读取直到下一个非数字字符(忽略任何领先的空格)。假设调用
scanf("%d", &val);
Run Code Online (Sandbox Code Playgroud)
如果您的输入流看起来像 {'\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,表示分配成功。
这是另一个丑陋的案例:假设您的用户输入了一个令人讨厌的长数字,例如“1234567890123456789012345678901234567890”。同样,您可能希望完全拒绝此输入,但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);
}
}
Run Code Online (Sandbox Code Playgroud)