C整数安全输入

时间:2018-04-30 08:47:02

标签: c integer scanf gets

如何使用scanfgets获得整数(特别是正数)的安全输入?我尝试了几种解决方案,每种解决方案都有一些问题。

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)),就会引发运行时错误。

那么使用scanfgets从控制台窗口检索整数值的正确方法是什么?

2 个答案:

答案 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的回答更容易,更安全。