当char输入而不是int时发生C无限循环 [英] C infinite loop when char input instead of int

查看:72
本文介绍了当char输入而不是int时发生C无限循环的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个C程序,该程序应该验证用户输入的值是1到8之间的一个int值.如果输入了整数,该函数就起作用了,但是当输入字符时,验证循环将永远重复.你能告诉我我在做什么错吗?

I have a C program which is supposed to validate that the input from the user is an int between 1 and 8. It works if an integer is entered, but when characters are entered the validation loop repeats forever. Can you tell what I am doing wrong?

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

推荐答案

每次进行用户输入时,必须考虑输入缓冲区中剩余的每个字符(此处为stdin ).由于scanf处理 input matching 失败的方式,在使用scanf(或系列)进行输入时尤其如此.发生任何一种情况时,不会再读取任何字符,并且任何有问题的字符都会留在输入缓冲区中未读取-只是在下次尝试读取时再次咬你(通常是如果您要在一个循环中进行输入,则会导致无限循环)

Any time you are taking user-input, you must account for each character that remains in the input buffer (stdin here). This is especially true when taking input with scanf (or family) due to the way scanf handles input or matching failures. When either occurs, no further characters are read, and any offending characters are left in the input buffer unread -- just waiting to bite you again on your next attempted read (generally resulting in an infinite loop if you are taking input within a loop)

此外,您必须了解每个转换说明符如何处理前导空格以及说明符是否占用了前导空格(例如 numeric 转换说明符和"%s"),但不是(其他所有,尤其是"%c""%[...]").

Further, you must understand how each conversion specier handles leading whitespace and whether leading whitespace is consumed by the specifier (e.g. numeric conversion specifiers and "%s") and which do not (all others, particularly "%c" and "%[...]").

这是推荐使用 line 功能(例如fgets(具有足够大小的缓冲区)或POSIX getline)进行用户输入的主要原因之一.读取并在填充的缓冲区中包含尾随'\n'.这完全消耗了该行,从而消除了任何不确定性,即其他字符仍保留在输入缓冲区中未读.然后,您可以将缓冲区传递给sscanf进行解析.这样就可以对(1)读取(我输入了吗?")和(2)从行中解析信息(是否包含我需要的信息?")进行独立验证.

This is one of the primary reasons a line-oriented function such as fgets (with an adequately sized buffer) or POSIX getline are recommended for taking user input. Both read and include the trailing '\n' in the buffer filled. This consumes the line completely, eliminating any uncertainty which additional characters remain in the input buffer unread. You can then pass the buffer to sscanf for parsing. This allows independent validation of both (1) the read ("did I get input?") and (2) the parse of information from the line ("did it contain the information I need?").

scanf.这意味着负责每次检查scanf 返回.您必须处理三个条件

scanf can be used, if used correctly. This means you are responsible for checking the return of scanf every time. You must handle three conditions

  1. (return == EOF)用户通过按 Ctrl + d (或在Windows Ctrl + z 上)生成手册EOF来取消输入,但请参见在Windows 10中CTRL + Z不会生成EOF(早期版本));
  2. (return < expected No. of conversions)发生匹配 input 失败.对于 matching 失败,您必须考虑将保留在输入缓冲区中的每个字符. (在输入缓冲区中向前扫描以读取和丢弃字符,直到找到'\n'EOF为止);最后
  3. (return == expected No. of conversions)表示读取成功-然后由您检查输入是否满足任何其他条件(例如,正整数,正浮点,在所需范围内,等等).
  1. (return == EOF) the user canceled input by generating a manual EOF by pressing Ctrl+d (or on windows Ctrl+z, but see CTRL+Z does not generate EOF in Windows 10 (early versions));
  2. (return < expected No. of conversions) a matching or input failure occurred. For a matching failure you must account for every character that will be left in your input buffer. (scan forward in the input buffer reading and discarding characters until a '\n' or EOF is found); and finally
  3. (return == expected No. of conversions) indicating a successful read -- it is then up to you to check whether the input meets any additional criteria (e.g. positive integer, positive floating-point, within a needed range, etc..).

利用代码,您可以通过以下方式进行输入-不断循环直到收到有效输入或用户通过生成手动EOF例如

Utilizing that with your code, you could take input in the following way -- looping continually until valid input is received or the user cancels by generating a manual EOF, e.g.

#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()以确保输入缓冲区为空并为下一次准备用户输入-可能会如此)

(note the use of the helper-function empty_stdin() -- and that even after a successful read, you should still empty_stdin() to insure the input buffer is empty and prepared for the next user input -- whatever that may be)

使用/输出示例

$ ./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.

If you do your job, you can successfully use scanf as needed.

或者,您可以使生活更轻松并使用fgets,然后测试缓冲区中的第一个字符(通过简单地取消引用指针)是否是有效的菜单选择,例如

Or, you can make your life much easier and use fgets and then test whether the first character in the buffer (by simply dereferencing the pointer) is a valid menu selection, e.g.

#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个字节....

Of course if a key gets stuck and the user enters more than 1023 characters -- characters will remain in the input buffer. But a simple test of whether the last character is '\n' and if not whether MAXC - 1 characters were read will let you know whether that is the case. Your choice, but fgets provides a much easier implementation. Just remember -- don't skimp on buffer size. I'd rather have a buffer 10,000 bytes too long that 1-byte too short....

这篇关于当char输入而不是int时发生C无限循环的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆