c - 使用 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
但根本不提示用户。
谢谢。
解决方案
不要使用isdigit()
- 它用于检查字符是否为数字。你读过一个数字。你必须检查返回值scanf()
——如果它不是 1,你就有问题了。根据您的要求,数字后面可能有各种各样的东西可能是也可能不是问题。我假设当您说“验证输入是整数”时,您希望允许多位数字和负数,并且由于您使用%i
而不是%d
,因此您可以使用八进制值(前导 0)或十六进制值(前导 0x 或 0X)也被输入。
请注意,如果您有:
int checker = scanf("%i", &i);
那么结果可能是1
,0
或EOF
. 如果结果是1
,那么您在可能的前导空格之后得到一个整数,包括可能的多个换行符。在数字之后和下一个换行符之前可能会有各种各样的“垃圾”。如果结果是0
,那么在跳过可能的空格(包括可能的多个换行符)之后,不是空格的第一个字符也不是整数的一部分(或者它可能是一个符号,而不是紧跟数字的符号)。如果结果是EOF
,则在可能读取空格之后检测到文件结尾,可能包括多个换行符,但在读取除空格之外的任何内容之前。
要明智地继续,您需要检查返回的值是否为1
. 即使这样,如果值超出该int
类型的有效范围,也可能会出现问题。
完整的要求还不完全清楚。但是,我将假设用户需要在输入的第一行输入一个数字,可能带有符号(-
或+
),可能是八进制(前导 0)或十六进制(前导 0x 或 0X),并且在数字之后和换行符之前最多有空格。并且该值必须在范围内INT_MIN
.. INT_MAX
?(scanf()
on overflow 的行为是未定义的——只是为了增加你的麻烦。)
用于此的正确工具是fgets()
或 POSIX
getline()
来读取行、
strtol()
转换为数字并isspace()
验证行尾。
请注意,正确使用strtol()
是相当棘手的。
在下面的代码中,请注意unsigned char
对isspace()
. 这确保了将有效值传递给函数,即使普通char
类型已签名并且用户输入的字符设置了高位,因此该值将转换为负整数。ispqrst()
任何或topqrst()
函数的有效输入是EOF
或 的范围unsigned char
。
...在所有情况下,参数都是 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)
您可能不需要它。您可以合理地决定不检测所有单独的条件,而是将一些错误聚合成较少数量的错误消息。
推荐阅读
- java - 显示时间而不转换为本地时区
- android - 使用 Expo 构建的应用程序中不存在/未加载数据库
- angular - 订阅的组件无法访问 observable 的响应
- visual-studio-code - VScode 图标窗口不会立即更新
- matlab - 使用 SNPS VCS 工具在 System verilog 中使用 MATLAB 脚本
- javascript - CSS延迟将元素的可见性从“可见”更改为“隐藏”
- angular - 从 ionic 5.4.16 中的 http 响应获取标头参数
- javascript - 如何为按钮列表赋予随机背景颜色?
- reactjs - 链接不重定向到 React 中的页面
- apache-spark - 使用yarn cluster模式提交spark应用