我有一个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));
}
}
答案 0 :(得分:2)
每次进行用户输入时,您都必须考虑保留在输入缓冲区中的每个字符(此处为stdin
)。由于scanf
处理 input 或 matching 失败的方式而使用scanf
(或族)输入时,尤其如此。发生任何一种情况时,不会再读取任何字符,并且任何有问题的字符都会留在输入缓冲区中 unread -等待下一次尝试读取时再次咬你(通常是如果您要在一个循环中进行输入,则会导致无限循环)
此外,您必须了解每个转换说明符如何处理前导空格,以及说明符是否占用了前导空格(例如 numeric 转换说明符和"%s"
),但不是(其他所有,尤其是"%c"
和"%[...]"
)。
这是推荐使用 line 函数(例如fgets
(具有足够大小的缓冲区)或POSIX getline
)来接受用户输入的主要原因之一。读取并在填充的缓冲区中包含结尾的'\n'
。这完全消耗了该行,从而消除了任何不确定性,即哪些额外的字符保留在输入缓冲区中未读。然后,您可以将缓冲区传递到sscanf
进行解析。这样就可以独立验证(1)读取(“我输入了吗?”)和(2)从行中解析信息(“其中是否包含我需要的信息?”)。
scanf
。这意味着您负责每次检查scanf
的退货。您必须处理三个条件
(return == EOF)
用户通过按 Ctrl + d (或在Windows Ctrl + z 上)生成手册EOF
来取消输入。 CTRL+Z does not generate EOF in Windows 10 (early versions)); (return < expected No. of conversions)
发生匹配或 input 失败。对于 matching 失败,您必须考虑将保留在输入缓冲区中的每个字符。 (在输入缓冲区中向前扫描以读取和丢弃字符,直到找到'\n'
或EOF
为止);最后(return == expected No. of conversions)
表示读取成功-然后由您检查输入是否满足任何其他条件(例如,正整数,正浮点,在所需范围内,等等。)。 / li>
利用代码,您可以通过以下方式进行输入-不断循环直到收到有效输入或用户通过生成手册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);
}
(注意)使用辅助功能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
完成工作后,可以根据需要成功使用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);
}
当然,如果键被卡住并且用户输入的字符超过1023
个,则字符将保留在输入缓冲区中。但是,如果对最后一个字符是否为'\n'
以及是否读取了MAXC - 1
个字符进行简单测试,就会知道情况是否如此。您可以选择,但是fgets
提供了更容易的实现。请记住-不要忽略缓冲区大小。我宁愿缓冲区太长10,000字节,缓冲区也太短1字节......
答案 1 :(得分:0)
如果我是您,在此特定过程中,我将检查ascii表而不是“ number”值。检查用户输入的内容是否是ascii表中从1到9的数字。从字面上看,在这种情况下可以解决您的问题。
#include <stdio.h>
int main(void)
{
int i;
char input;
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(" %c", &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'));
}
}
答案 2 :(得分:0)
如第一个解决方案所示,字符保留在缓冲区中,并且您无法访问新行以接受新值。请在下面找到我的解决方案,基本上可以找到\ n表示重新调用scanf时接受新输入的新行
#include <stdio.h>
int main(void)
{
int x;
int result;
while (1){
printf("Positive Number: ");
result = scanf("%d", &x);
if (result == 0) {
// find the new line to recieve new input
while(fgetc(stdin) != '\n');
}
if (x>0) {
break;
} else {
printf("Error, please enter a positive number.\n");
}
}
}