C输入char而不是int时出现无限循环

1l5u6lss  于 2023-01-01  发布在  其他
关注(0)|答案(4)|浏览(149)

我有一个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));
  }
}
exdqitrt

exdqitrt1#

任何时候您接受用户输入时,必须考虑保留在输入缓冲区中的每个字符(此处为stdin)。使用scanf获取输入时尤其如此(或系列),原因是scanf处理 * 输入 * 或 * 匹配 * 失败的方式。当发生其中任何一种情况时,* 不再读取任何字符 *,并且任何有问题的字符都留在输入缓冲区中unread--只是等着下次尝试读取时再咬你一口(如果在循环中获取输入,通常会导致无限循环)
此外,您必须了解每个 * 转换说明符 * 如何处理 * 前导空格 *,以及前导空格是否被说明符占用(例如 * numeric * 转换说明符和"%s"),哪些不被占用(所有其他说明符,特别是"%c""%[...]")。
这是使用fgets这样的 * 面向行 * 函数的主要原因之一(具有足够大小的缓冲区)或POSIX getline来获取用户输入。这两种方法都读取并包括填充缓冲区中的尾随'\n'。这将完全消耗该行,消除了输入缓冲区中哪些字符未被读取的不确定性。然后,您可以将缓冲区传递给sscanf进行解析。这允许对(1)读取("我得到输入了吗?")和(2)行信息的解析("它包含我需要的信息了吗?")进行独立验证。
scanf可以使用,如果使用正确的话,这意味着有责任检查scanf * 每次 * 的返回,您必须处理 * 三个条件 *

  1. (return == EOF)用户通过按下Ctrl + d(或在Windows上按下Ctrl + z,但参见CTRL+Z does not generate EOF in Windows 10 (early versions))生成手动EOF取消输入;
  2. (return < expected No. of conversions)出现 * matching * 或 * input * 故障。对于 * matching * 故障,必须考虑将留在输入缓冲区中的每个字符。(在输入缓冲区中向前扫描,读取并丢弃字符,直到找到'\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-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); 
}

注意helper-function 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字节太短...

hts6caw3

hts6caw32#

如果我是你,在这个特殊的追逐中,我会检查,而不是一个“数字”的值,ascii表。检查用户输入的,如果字面上是一个数字,从1到9在ascii表。字面上,解决你的问题,在这种情况下。

#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'));
  }
}
mnemlml8

mnemlml83#

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

r3i60tvu4#

这是我第一次回答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.CAN\n");
  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.CAN\n");
      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));
  }
}

相关问题