当char输入而不是int时,C无限循环



我有一个C程序,它应该验证用户的输入是1到8之间的int。如果输入一个整数,它会起作用,但当输入字符时,验证循环会永远重复。你能说出我做错了什么吗?

#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.CANn");
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.CANn");
printf("Which one do you want to pick?");
scanf(" %d", &input);
}while((input < 1) || (input > 8));
}
}

任何时候进行用户输入时,都必须考虑输入缓冲区中剩余的每个字符(此处为stdin(。由于scanf处理输入匹配失败的方式,在使用scanf(或族(进行输入时尤其如此。当发生任何一种情况时,都不会读取更多字符,任何有问题的字符都会留在输入缓冲区中未读——只是等待在下一次尝试读取时再次咬你(如果你在循环中输入,通常会导致无限循环(

此外,您必须了解每个转换说明符是如何处理前导空白的,以及前导空白是否被说明符使用(例如数字转换描述符和"%s"(,哪些不使用(所有其他,特别是"%c""%[...]"(。

这是建议使用面向行的函数(如fgets(具有足够大小的缓冲区(或POSIXgetline(来获取用户输入的主要原因之一。两者都读取并将尾随的'n'包括在已填充的缓冲器中。这完全消耗了行,消除了输入缓冲区中哪些附加字符未读的不确定性。然后,您可以将缓冲区传递给sscanf进行解析。这允许独立验证(1(读取("我得到输入了吗?"(和(2(解析行中的信息("它包含我需要的信息吗?"。

如果使用正确,则可以使用scanf。这意味着负责每次检查scanf返回。您必须处理三个条件

  1. (return == EOF)用户通过按Ctrl+d生成手动EOF来取消输入(或在窗口Ctrl+z
  2. (return < expected No. of conversions)出现匹配输入故障。对于匹配失败,您必须考虑到将留在输入缓冲区中的每个字符。(在输入缓冲器中向前扫描,读取并丢弃字符,直到找到'n'EOF(;最后
  3. (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-EDUn"
"  2-COMn"
"  3-ORGn"
"  4-GOVn"
"  5-MILn"
"  6-CNn"
"  7-COM.CNn"
"  8.CANnn"
"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: %dn", 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-EDUn"
"  2-COMn"
"  3-ORGn"
"  4-GOVn"
"  5-MILn"
"  6-CNn"
"  7-COM.CNn"
"  8.CANnn"
"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 inputn", stderr);
continue;
}
input = *buf - '0';     /* convert char to integer */
break;
}
printf ("nvalid input: %dn", input); 
}

当然,如果一个键被卡住,并且用户输入的字符超过1023,则字符将保留在输入缓冲区中。但是,一个简单的测试最后一个字符是否是'n',如果不是,是否读取了MAXC - 1字符,就会让你知道情况是否如此。您可以选择,但fgets提供了一个更容易的实现。请记住,不要吝啬缓冲区大小。我宁愿有一个缓冲区,10000字节太长,1字节太短。。。。

这是我第一次回答stackoverflow,所以我希望你能对它有所帮助。为了修复这个错误,只需在输入后插入getchar();来清理缓冲区,它就会修复问题。

#include <stdio.h>
int main(void)
{
int i;
int 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.CANn");
printf("Which one do you want to pick?");
scanf(" %d", &input);
getchar(); //this line will clean the buffer, only fixes the error for a char 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.CANn");
printf("Which one do you want to pick?");
scanf(" %d", &input);
getchar(); // will fix the error only if the input is a string, but it will duplicate the lines before..
}while((input < 1) || (input > 8));
}
}

如果我是你,在这个特定的追逐中,我会检查ascii表,而不是"数字"值。检查用户输入的内容是否是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.CANn");
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.CANn");
printf("Which one do you want to pick?");
scanf(" %d", &input);
}while((input < '1') || (input > '8'));
}
}

如第一个解决方案所示,字符保留在缓冲区中,您将无法访问新行以接受新值。请找到我的解决方案,在下面我基本上找到了当再次调用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");
}
}
}

最新更新