首页 > 解决方案 > 使用 scanf 和 isdigit 进行 int 验证

问题描述

我想验证输入是int.

理想情况下,如果验证失败,我希望它向用户提示。

我试过这个:

#include <stdio.h>
#include <ctype.h>

int main(void)
{
    int i;
    printf("enter a number: ");
    scanf("%i", &i);
    (isdigit(i)) ? printf("%i is a digit\n", i) : printf("%i is NOT a digit\n", i);
}

它不符合我的要求。

结果如下:

> enter a number: 1
> 1 is NOT a digit

但神奇的是:

> enter a number: 54
> 54 is a digit

为什么?如何以正确的方式进行验证?

== 更新 ==

我尝试了一些不使用isdigit但检查scanf结果的建议,它的工作原理如下:

int i, checker;
printf("enter a number: ");
checker = scanf("%i", &i);
(checker) ? printf("%d Is digit\n", i) : printf("%d Is NOT digit\n", i);

这是结果

enter a number: 12
12 Is digit
enter a number: asdf
220151845 Is NOT digit

但它未能通过 while 循环验证,

int i, checker;
do
{
    printf("enter a number: ");
    checker = scanf("%i", &i);
} while (!checker);
printf("number : %i\n", i);

特别是当它不是数字时,它会变成无限循环printf但根本不提示用户。

谢谢。

标签: c

解决方案


不要使用isdigit()- 它用于检查字符是否为数字。你读过一个数字。你必须检查返回值scanf()——如果它不是 1,你就有问题了。根据您的要求,数字后面可能有各种各样的东西可能是也可能不是问题。我假设当您说“验证输入是整数”时,您希望允许多位数字和负数,并且由于您使用%i而不是%d,因此您可以使用八进制值(前导 0)或十六进制值(前导 0x 或 0X)也被输入。

请注意,如果您有:

int checker = scanf("%i", &i);

那么结果可能是1,0EOF. 如果结果是1,那么您在可能的前导空格之后得到一个整数,包括可能的多个换行符。在数字之后和下一个换行符之前可能会有各种各样的“垃圾”。如果结果是0,那么在跳过可能的空格(包括可能的多个换行符)之后,不是空格的第一个字符也不是整数的一部分(或者它可能是一个符号,而不是紧跟数字的符号)。如果结果是EOF,则在可能读取空格之后检测到文件结尾,可能包括多个换行符,但在读取除空格之外的任何内容之前。

要明智地继续,您需要检查返回的值是否为1. 即使这样,如果值超出该int类型的有效范围,也可能会出现问题。

完整的要求还不完全清楚。但是,我将假设用户需要在输入的第一行输入一个数字,可能带有符号(-+),可能是八进制(前导 0)或十六进制(前导 0x 或 0X),并且在数字之后和换行符之前最多有空格。并且该值必须在范围内INT_MIN.. INT_MAX?(scanf()on overflow 的行为是未定义的——只是为了增加你的麻烦。)

用于此的正确工具是fgets()或 POSIX getline()来读取行、 strtol()转换为数字并isspace()验证行尾。

请注意,正确使用strtol()是相当棘手的。

在下面的代码中,请注意unsigned charisspace(). 这确保了将有效值传递给函数,即使普通char类型已签名并且用户输入的字符设置了高位,因此该值将转换为负整数。ispqrst()任何或topqrst()函数的有效输入是EOF或 的范围unsigned char

C11 §7.4 字符处理<ctype.h>¶1

...在所有情况下,参数都是 an int,其值应可表示为 anunsigned char或应等于宏的值EOF。如果参数有任何其他值,则行为未定义。

GNU C 库倾向于保护粗心大意的人,但您不应该依赖于标准 C 库的保姆。

#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void)
{
    char line[4096];    /* Make it bigger if you like */

    printf("Enter a number: ");
    if (fgets(line, sizeof(line), stdin) == 0)
    {
        fprintf(stderr, "Unexpected EOF\n");
        exit(EXIT_FAILURE);
    }
    errno = 0;
    char *eon;    /* End of number */
    long result = strtol(line, &eon, 0);
    if (eon == line)
    {
        fprintf(stderr, "What you entered is not a number: %s\n", line);
        exit(EXIT_FAILURE);
    }
    if (sizeof(int) == sizeof(long))
    {
        if ((result == LONG_MIN || result == LONG_MAX) && errno == ERANGE)
        {
            fprintf(stderr, "What you entered is not a number in the range %ld..%+ld: %s\n",
                    LONG_MIN, LONG_MAX, line);
            exit(EXIT_FAILURE);
        }
    }
    else
    {
        if ((result == LONG_MIN || result == LONG_MAX) && errno == ERANGE)
        {
            fprintf(stderr, "What you entered is not a number in the range %ld..%+ld,\n"
                    "let alone in the range %d..%+d: %s\n",
                    LONG_MIN, LONG_MAX, INT_MIN, INT_MAX, line);
            exit(EXIT_FAILURE);
        }
    }
    char c;
    while ((c = *eon++) != '\0')
    {
        if (!isspace((unsigned char)c))
        {
            fprintf(stderr, "There is trailing information (%c) after the number: %s\n",
                    c, line);
            exit(EXIT_FAILURE);
        }
    }
    if (result < INT_MIN || result > INT_MAX)
    {
        fprintf(stderr, "What you entered is outside the range %d..%+d: %s\n",
                INT_MIN, INT_MAX, line);
        exit(EXIT_FAILURE);
    }
    int i = result;   /* No truncation given prior tests */
    printf("%d is a valid int\n", i);
    return(EXIT_SUCCESS);
}

对于相当大的奇怪数字和非数字输入集合,这似乎可以正常工作。该代码同时处理 32 位系统sizeof(long) == sizeof(int)和 LP64 64 位系统,sizeof(long) > sizeof(int)您可能不需要它。您可以合理地决定不检测所有单独的条件,而是将一些错误聚合成较少数量的错误消息。


推荐阅读