【问题标题】:C - Input Verification in loop acting weirdC - 循环中的输入验证行为怪异
【发布时间】:2015-03-23 17:00:52
【问题描述】:

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

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

下面是代码。不断打印的部分是if( isalpha(uNum) ){...。对于我的代码的此版本,将输入置于 30 以上和 1 以下工作正常,但输入“abc”之类的内容会不断打印超过 30 的消息(即“值必须最多为 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;
}

此时我想知道简单地更改“else break;”是否更有意义。语句(这意味着输入是好的)具有 =1 的复合条件,以便新的 else 语句负责第一个 if 应该做什么。

【问题讨论】:

  • scanf() 可能会失败添加检查并且您的许多问题都会得到解答
  • scanf() 会失败是什么意思?我不明白这需要什么样的检查。
  • if(scanf("%d",&amp;uNum) == 1) { // Scanf 成功 }
  • 好的,但是从那里继续我将如何检查非数字输入,因为这是项目的要求之一?具体来说,我们必须在适当的位置打印“您的条目必须以数字开头....”的消息
  • isalpha(int n) 很可能没有做你想做的事。 scanf() 已经尝试转换为整数,但可能失败了,流指向的第一个字符不能是整数的一部分。你真的应该检查scanf() 是否成功。您的while 循环会很高兴地丢弃所有内容,甚至是有效的整数字符,直到换行符...可能还有其他一些东西...其中一些会通过打开编译器警告并注意它们来捕获...

标签: c loops if-statement input verification


【解决方案1】:

scanf 在尝试在循环中获取输入时可能会有点棘手,因为如果在调用 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,只需调整测试即可。

【讨论】:

  • 它将接受来自1X1 -- 是的。就像strtol, strtoul, etc.. 一样。如果现在不接受这是一个新要求,我们可以重新处理格式字符串或完全跳过scanf 并使用简单的指针。
  • 非常非常感谢。由于您的示例显示了如何检查 scanf 是必要的,我能够构建我的循环以正常工作。我还要感谢 Gopi 和 twalberg 最初的建议以及其他所有花时间回答我的人。非常感谢大家。
  • 很高兴为您提供帮助,祝您编程顺利。 C 是一门很棒的学习语言。
【解决方案2】:

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

scanf() 的返回值告诉您匹配格式中的项目数量,其中包括在读取任何数字之前是否发生匹配失败。但是,scanf() 不会告诉您任何事情,关于输入中接下来的内容(无法匹配)。

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

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

【讨论】:

    【解决方案3】:

    根据评论者的建议,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
    

    【讨论】:

    • 根据预期的确切输入形式,!isspace(c) 可能是比c != '\n' 更合适的测试。
    • @JohnBollinger 好点,也许在某些操作系统中,下一个字符可能是 '\r'
    猜你喜欢
    • 2012-11-10
    • 1970-01-01
    • 2020-12-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-08
    • 1970-01-01
    • 2021-05-13
    相关资源
    最近更新 更多