如何使用scanf
或gets
获得整数(特别是正数)的安全输入?我尝试了几种解决方案,每种解决方案都有一些问题。
1。使用getchar()
删除字符串输入
int safeInput() {
int input;
scanf("%d", &input);
while(getchar() != '\n');
return input;
}
此方法有效地处理字符串输入,但是,如果输入3a
之类的字符串,则input
的值变为3
,这不是真正的异常句柄。
2。将输入检索为字符串,然后转换为整数值。
int safeInput() {
char[200] input, safe_input;
gets(input);
// I know about the security issue about gets - but it's not the point.
int i = 0;
while (1) {
if (input[i] >= 48 && input[i] <= 57) safe_input[i] = input[i];
else break;
i++;
}
return atoi(safe_input);
}
如果输入长度超过分配给input
的字符串,则此方法存在无法处理的问题。
第3。如果使用指针定义字符串怎么办?
我担心通过指针定义input
,例如char *input;
。但是,一旦我执行gets(input)
(或scanf("%s", input)
),就会引发运行时错误。
那么使用scanf
或gets
从控制台窗口检索整数值的正确方法是什么?
答案 0 :(得分:2)
答案取决于安全的具体含义。如果您想捕获任何可能的输入错误,您唯一的选择是使用strtol()
系列的功能,甚至可以进行范围检查。在我的beginners' guide away from scanf()
中,我描述了它的用法。
这里的代码适合您在此处尝试的内容,并附有评论:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
// return success as boolean (0, 1), on success write result through *number:
int safeInput(int *number)
{
long a;
char buf[1024]; // use 1KiB just to be sure
if (!fgets(buf, 1024, stdin))
{
// reading input failed:
return 0;
}
// have some input, convert it to integer:
char *endptr;
errno = 0; // reset error number
a = strtol(buf, &endptr, 10);
if (errno == ERANGE)
{
// out of range for a long
return 0;
}
if (endptr == buf)
{
// no character was read
return 0;
}
if (*endptr && *endptr != '\n')
{
// *endptr is neither end of string nor newline,
// so we didn't convert the *whole* input
return 0;
}
if (a > INT_MAX || a < INT_MIN)
{
// result will not fit in an int
return 0;
}
// write result through the pointer passed
*number = (int) a;
return 1;
}
答案 1 :(得分:0)
首先,如果您想要安全输入,请勿使用gets
。当你可以使用fgets
时,说你知道这些问题并不是一个真正的借口。接下来,诀窍是尝试在int之后读取一个非空白字符:如果找不到任何人,那么在行之后没有任何内容。
int safeInput(int *input) { // the return value is the indicator of failed read
int c;
char dummy[2]; // never forget the terminating null!
if (scanf("%d%1s", input, dummy) == 1) return 1;
// in case of error, skip anything up to end of line or end of file
while (((c = fgetc(stdin)) != '\n') && (c != EOF));
return 0;
}
这里的好处是,当scanf
返回1时,%1s
已吃掉任何直到行尾的内容,包括终止&#39; n'
。但这有一个主要缺点:scanf
只会在流结束时或在读取一个额外的(非空白)字符后结束。因此,Felix Palmen的回答更容易,更安全。