C - 循环中的输入验证行为很奇怪

C - Input Verification in loop acting weird

我正在编写一个输入例程,请求用户输入 1 到 30 之间的数字,这意味着发出错误消息并重新提示用户输入,直到收到有效输入。我使用一个循环来读取输入并根据各种情况检查它,一旦收到适当的输入就退出循环。

检查数字是否在范围内的案例工作(或确实工作)非常好,但是当我检查以确保用户没有输入任何非数字的东西时,整个事情几乎爆炸了我的脸,循环似乎很奇怪。一开始我被告知这是一个没有关闭输入缓冲区的问题,所以我添加了这个修复程序,但是尽管第一次满足了条件,它也不会跳回到循环的前面以允许更改输入-- 相反,它会立即跳转到一个分支并在订单上连续打印该消息或每秒打印数千次。

下面是代码。不断打印的部分是 if( isalpha(uNum) ){...。对于我的代码的这次修订,将输入设置为高于 30 和低于 1 就可以正常工作,但是输入 "abc" 之类的内容会不断打印超过 30 的消息(即 "Value must be at most 30...")。当我取出清除缓冲区的语句( while(getchar() != '\n'); )时,上述消息不断地无休止地发送垃圾邮件。有人可以帮我弄清楚为什么 and/or 我需要做些什么来修复它吗?

int get_seed(void)
{
    int uNum;
    printf("Please enter an integer between 1 and 30:\n");
    while(1) {
        scanf("%d",&uNum);
        while(getchar() != '\n');
        if( isalpha(uNum) ){
            printf("Your entry must start with a digit; try again\n");
        }
        else if( (uNum < 1) || (uNum > 30) ){
            if(uNum<1){
                printf("Value must be at least one; try again\n");
            } else {
                printf("Value must be at most 30; try again\n");
            }
            continue;
        }
        else
            break;
    }
    return uNum;
}

在这一点上,我想知道简单地更改 "else break;" 语句(这意味着输入良好)以具有 <=30 和 >=1 的复合条件是否更有意义,以便一个新的 else 语句负责第一个 if 应该做什么。

scanf 尝试在循环中获取输入时可能会有点棘手,因为如果您未能在调用 scanf 之间清空标准输入,则可能会出现无限循环。这并不完美,但它更接近您正在寻找的东西。关键是利用return到scanf判断是否匹配失败:

#include <stdio.h>

#define MAXNUM 30

int main () {

    int uNum = 0;

    printf ("\nEnter a number between 1 and %d:\n", MAXNUM);

    while (printf ("\n  uNum: "))
    {
        int rtn = scanf ("%d", &uNum);
        int c = 0;

        /* check for matching failure, empty input buffer */
        if (rtn == 0) do { c = getchar(); } while ( c != '\n' && c != EOF);

        if (uNum > 0 && uNum < MAXNUM + 1) break;
    }

    printf ("\nValue : %d\n\n", uNum);

    return 0;

}

输出

$ ./bin/scf130

Enter a number between 1 and 30:

  uNum: a

  uNum: -1

  uNum: 31

  uNum: 18

Value : 18

注意:这将接受数字1 ... 30,如果你想要2 ... 29只需调整测试。

格式字符串 "%d" 指示 scanf() 跳过任何前导空格,并尝试将以第一个非空格开头的字符转换为带符号的十进制整数。只要输入实际上具有那种形式,这一切都很好,但它不适合读取或验证一般输入,因为它 停止 当它遇到它无法匹配的字符时图案。这可能发生在任何数字被读取之前或之后;如果之前,则*uNum.

中没有任何记录

scanf() 的 return 值告诉您匹配了格式中的多少项,其中包括在读取任何数字之前是否发生匹配失败。 scanf() 不会告诉您 任何内容,但是,关于输入中的下一个内容(无法匹配)。

此外,如果 scanf() 成功 确实 匹配一个数字,那么您已经知道匹配的部分实际上是数字。因此,对 isalpha() 的调用不仅没有必要,而且 是错误的 。它会询问代码为 uNum 的字符是否是字母,但这根本不是您想知道的。

此外,不清楚确切您期望的输入形式,但while(getchar() != '\n');很可能不是您想要的想。如果您希望用户键入一个响应然后按 enter 键终止它,那么您最好通过 fgets() 一次读取一行,然后检查整行。您可能会发现 strtol() 函数对于将输入转换为数字以及确定是否存在任何会使输入无效的尾随字符(例如“1foo”)很有用。

根据评论者的建议,scanf() 中的 return 值将告诉您成功转换的字段数。如果输入的数字前面有非数字字符,则输入失败。如果输入的数字后跟一个非数字字符,转换会成功,但会在此时停止。

所以这个解决方案的作用是检查输入的数字后面是什么字符。如果是newline,则输入成功

#include<stdio.h>

int main()
{
    int n;
    char c;
    printf ("Enter a number: ");
    if (2 != scanf("%d%c", &n, &c)) {
        printf ("Invalid number input\n");
        return 1;
    }
    if (c != '\n') {
        printf ("Invalid number input\n");
        return 1;
    }
    if (n<1 || n>30) {
        printf ("Valid range is 1..30\n");
        return 1;
    }
    printf("You entered %d\n", n);
    return 0;
}

示例程序运行:

Enter a number: a12
Invalid number input

Enter a number: 12a
Invalid number input

Enter a number: 42
Valid range is 1..30

Enter a number: 12
You entered 12