efu*_*ddy 4 c validation scanf infinite-loop
我有一个 C 程序,它应该验证用户的输入是否是 1 到 8 之间的整数。如果输入整数,它就可以工作,但是当输入字符时,验证循环将永远重复。你能告诉我我做错了什么吗?
#include <stdio.h>
int main(void)
{
int i;
int input;
domainEntry *myDomains = buildDomainDB();
printf("You have the choice between the"
" following top domains: 1-EDU, 2-COM"
", 3-ORG, 4-GOV, 5-MIL, 6-CN, 7-COM.CN, 8.CAN\n");
printf("Which one do you want to pick?");
scanf(" %d", &input);
if(input < 1 || input > 9)
{
do
{
printf("Invalid input. Please try again.");
printf("You have the choice between the"
" following top domains: 1-EDU, 2-COM"
", 3-ORG, 4-GOV, 5-MIL, 6-CN, 7-COM.CN, 8.CAN\n");
printf("Which one do you want to pick?");
scanf(" %d", &input);
}while((input < 1) || (input > 8));
}
}
Run Code Online (Sandbox Code Playgroud)
每当您进行用户输入时,您都必须考虑输入缓冲区中保留的每个字符(stdin此处)。scanf由于scanf处理输入或匹配失败的方式,在接受输入(或族)时尤其如此。当发生任何一种情况时,不会再读取任何字符,并且任何有问题的字符都将留在输入缓冲区中未读- 只是等待在下一次尝试读取时再次咬住您(如果您在循环内获取输入,通常会导致无限循环)
此外,您必须了解每个转换说明符如何处理前导空格,以及说明符是否消耗前导空格(例如数字转换说明符 和"%s"),哪些不消耗(所有其他转换说明符,特别是"%c"和"%[...]")。
这是建议使用面向行的函数(例如fgets(具有足够大小的缓冲区)或 POSIX)getline来获取用户输入的主要原因之一。'\n'两者都读取并包含填充的缓冲区中的尾随。这完全消耗了该行,消除了输入缓冲区中未读取的附加字符的任何不确定性。然后您可以将缓冲区传递给sscanf解析。这允许独立验证(1)读取(“我得到输入了吗?”)和(2)对来自该行的信息的解析(“它包含我需要的信息吗?”)。
scanf如果使用正确的话,是可以使用的。这意味着您有责任检查每次的返回。你必须处理三个条件scanf
(return == EOF)用户通过EOF按Ctrl+d(或在 Windows 上)生成手册来取消输入Ctrl+z,但请参阅CTRL+Z 在 Windows 10(早期版本)中不会生成 EOF)生成手册来取消输入;(return < expected No. of conversions)发生匹配或输入失败。对于匹配失败,您必须考虑输入缓冲区中剩余的每个字符。(在输入缓冲区中向前扫描,读取并丢弃字符,直到找到'\n'或);EOF最后(return == expected No. of conversions)表示读取成功——然后由您检查输入是否满足任何附加条件(例如正整数、正浮点数、在所需范围内等)。在您的代码中使用它,您可以通过以下方式获取输入 - 不断循环直到收到有效输入或用户通过生成手册取消EOF,例如
#include <stdio.h>
void empty_stdin (void) /* simple helper-function to empty stdin */
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
int main(void)
{
int input = 0,
rtn = 0; /* variable to save scanf return */
// domainEntry *myDomains = buildDomainDB();
for (;;) { /* loop continually until valid input or EOF */
printf ("\nSelect top level domain:\n"
" 1-EDU\n"
" 2-COM\n"
" 3-ORG\n"
" 4-GOV\n"
" 5-MIL\n"
" 6-CN\n"
" 7-COM.CN\n"
" 8.CAN\n\n"
"choice: ");
rtn = scanf (" %d", &input); /* save return */
if (rtn == EOF) { /* user generates manual EOF */
fputs ("(user canceled input.)\n", stderr);
return 1;
}
else if (rtn == 0) { /* matching failure */
fputs (" error: invalid integer input.\n", stderr);
empty_stdin();
}
else if (input < 1 || 8 < input) { /* validate range */
fputs (" error: integer out of range [1-8]\n", stderr);
empty_stdin();
}
else { /* good input */
empty_stdin();
break;
}
}
printf ("\nvalid input: %d\n", input);
}
Run Code Online (Sandbox Code Playgroud)
(注意辅助函数的使用empty_stdin()——即使在成功读取之后,您仍然应该empty_stdin()确保输入缓冲区为空并为下一个用户输入做好准备——无论是什么)
使用/输出示例
$ ./bin/getintmenu
Select top level domain:
1-EDU
2-COM
3-ORG
4-GOV
5-MIL
6-CN
7-COM.CN
8.CAN
choice: edu
error: invalid integer input.
Select top level domain:
1-EDU
2-COM
3-ORG
4-GOV
5-MIL
6-CN
7-COM.CN
8.CAN
choice: 9
error: integer out of range [1-8]
Select top level domain:
1-EDU
2-COM
3-ORG
4-GOV
5-MIL
6-CN
7-COM.CN
8.CAN
choice: 4
valid input: 4
Run Code Online (Sandbox Code Playgroud)
如果你做好了你的工作,你就可以scanf根据需要成功使用。
或者,您可以使您的生活变得更轻松,然后使用fgets然后测试缓冲区中的第一个字符(通过简单地取消引用指针)是否是有效的菜单选择,例如
#include <stdio.h>
#define MAXC 1024 /* read buffer max characters */
int main (void) {
int input = 0;
char buf[MAXC];
// domainEntry *myDomains = buildDomainDB();
for (;;) { /* loop continually until valid input or EOF */
fputs ("\nSelect top level domain:\n"
" 1-EDU\n"
" 2-COM\n"
" 3-ORG\n"
" 4-GOV\n"
" 5-MIL\n"
" 6-CN\n"
" 7-COM.CN\n"
" 8.CAN\n\n"
"choice: ", stdout);
if (!fgets (buf, MAXC, stdin)) {
fputs ("(user canceled input.)\n", stderr);
return 1;
}
if (*buf < '1' || '8' < *buf) { /* check 1st char, validate range */
fputs (" error: invalid input\n", stderr);
continue;
}
input = *buf - '0'; /* convert char to integer */
break;
}
printf ("\nvalid input: %d\n", input);
}
Run Code Online (Sandbox Code Playgroud)
当然,如果按键卡住并且用户输入了多个1023字符——字符将保留在输入缓冲区中。但是,通过简单测试最后一个字符是否是最后一个字符'\n'以及如果不是,是否MAXC - 1读取了字符,您就可以知道情况是否如此。您的选择,但fgets提供了更容易的实现。请记住——不要吝惜缓冲区大小。我宁愿缓冲区太长 10,000 字节,也不愿 1 字节太短......
| 归档时间: |
|
| 查看次数: |
11694 次 |
| 最近记录: |