【问题标题】:Reading until I manage to enter an integer阅读直到我设法输入一个整数
【发布时间】:2017-02-28 20:10:21
【问题描述】:

我是 C 的新手,我曾经在 Python 中工作,我想看看我读到的东西是否是整数。如果没有,请阅读,直到我设法输入一个数字。

我做了一些研究,发现函数 scanf 实际上如果读取正确则返回 1,否则返回 0。 所以,我写了这段代码,我不明白为什么这是一个无限循环,在控制台上写“Give an integer”

#include <stdio.h>

int main() {
    int a;
    int b = 1;
    do {
        printf("Give an integer\n");
        b = scanf("%d", &a);
    } while (b == 0);
}

【问题讨论】:

  • 问题是scanf() 不消费与你的字段描述符不匹配的数据。因此,当您循环返回重试时,仍然等待处理相同的不匹配数据。
  • 有效!但我真的不明白为什么:))

标签: c integer scanf


【解决方案1】:

scanf() 的问题在于,当遇到像"%d" 这样的大多数说明符的第一个空格时,它会停止读取,所以它会留在输入中,这就是为什么如果你不丢弃,再次读取会导致问题这样的空白,因为它会在你下次调用它时立即返回。当您按 EnterReturn'\n' 换行符(或换行)时,会引入一个强制空白。

如果你想读取一个整数并确保你做到了,你可以这样尝试

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

int
main(void)
{
    long int value; // You can adapt this to use `int'
                    // but be careful with overflow
    char line[100];
    while (fgets(line, sizeof(line), stdin) != NULL) {
        char *endptr;
        value = strtol(line, &endptr, 10);
        if ((isspace(*endptr) != 0) && (endptr != line))
            break;
    }
    fprintf(stdout, "%ld\n", value);
    return 0;
}

阅读strtol()的手册来理解这段代码

您可以按照 cmets 中的建议从输入中删除空白字符,但恕我直言,这更难,scanf() 仍然存在其他问题,例如处理不直接的空输入。

【讨论】:

  • scanf() 忽略空白字符充其量是一种误导,并且就scanf() 的OP use 而言,它并不能解释问题他描述。
  • @JohnBollinger 你完全正确!我修复了它,我认为现在更容易理解 OP 代码的问题。
  • (isspace(*endptr) != 0) 允许输入有效的"123 456"。也许(*endptr = '\n' || *endptr = '\0')
【解决方案2】:

我不明白为什么这是一个无限循环,在控制台上写“Give an intiger”

问题在于scanf() 不会使用无法与指定格式匹配的数据。它在输入流中留下未读的这些字符。因此,如果您尝试从相同格式的同一流中再次读取,而不通过其他方式消耗至少一个字符,那么您可以确定输入将再次不匹配。然后再次。又一次。

为避免您的无限循环,您需要在每次匹配失败后消耗至少一个非匹配输入字符。有很多方法可以做到这一点;这是一个相当简单的:

#include <stdio.h>

int main() {
    int a;

    do {
        printf("Give an intiger\n");
        if (scanf("%d", &a)) {
            // breaks from the loop on a successful match or an error
            break;
        }
        // consume the remainder of one line of input without storing it
        if (scanf("%*[^\n]") == EOF) {
            break;
        }
    } while (1);
}

这会消耗遇到不匹配输入的行的整个剩余部分,与许多替代方案相比,这对于某些输入会产生不那么令人惊讶的交互行为。

如果您喜欢编写简洁的代码,或者如果您不喜欢在循环中间使用break,那么您可以编写类似这样的代码:

#include <stdio.h>

int main() {
    int a;

    do {
        printf("Give an intiger\n");
    } while ((scanf("%d", &a) == 0) && (scanf("%*[^\n]") != EOF));
}

由于&amp;&amp; 运算符短路,第二个scanf() 调用将仅在第一个返回零时执行,并且循环将在第一次迭代后退出,其中第一个scanf() 调用返回非零或第二个返回EOF(表示错误)。

【讨论】:

  • 在 scanf 之后添加 getchar() 就可以了。但是为什么函数 scanf 不消耗特定格式的数据呢?这是错误还是语言设计决定?
  • @DANIROLST,在失败的scanf() 之后添加getchar() 将避免无限 循环,但它仍然会为某些输入产生令人惊讶的行为。例如,尝试使用“abc123”。
  • @DANIROLST,C 标准明确规定了scanf() 行为的这一方面。而且,这是有道理的:如果scanf() 使用了不匹配的数据,那么该数据将丢失,没有任何机制可以取回它。此外,如果scanf() 想要消耗一些不匹配的输入,那么您认为它应该消耗多少?没有适合所有情况的答案。
  • @DANIROLST:在许多文本处理应用程序中,您希望保留不匹配的输入并使用不同的格式重试,而不是丢弃它。
  • 哦,现在说得通了。我问了这个问题,因为我从来没有遇到过这样的情况,当我应该使用一些用户未正确提供的数据时,我认为应该丢失信息。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-02-12
  • 2021-09-09
  • 2023-03-06
  • 1970-01-01
  • 2017-10-02
  • 2020-01-15
  • 1970-01-01
相关资源
最近更新 更多