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

时间:2015-03-23 17:00:53

标签: c loops if-statement input verification

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

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

以下是代码。连续打印的部分是if( isalpha(uNum) ){...。对于我的代码的这个版本,将输入放在30以上和1以下工作就好了,但抛出像“abc”这样的东西会不断地打印超过30的消息(即“Value必须至多30 ......”)。当我取出语句来清除缓冲区(while(getchar() != '\n');)时,上述消息会不断发送垃圾邮件。有人可以帮助我解决为什么这样做和/或我需要做些什么来解决它?

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

此时我想知道简单地改变“其他休息时间”会更有意义吗?语句(这意味着输入是好的)具有&lt; = 30和&gt; = 1的复合条件,以便新的else语句处理第一个if应该做什么。

3 个答案:

答案 0 :(得分:1)

当尝试在循环中获取输入时,如果在scanf的调用之间未能清空stdin,则由于可能存在无限循环,因此

scanf可能有点棘手。这并不完美,但它更接近你所寻找的东西。关键是利用返回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

答案 1 :(得分:1)

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

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

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

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

答案 2 :(得分:1)

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

所以这个解决方案的作用是检查数字输入后面的字符。如果是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