在C中请求用户输入

时间:2018-09-24 01:55:22

标签: c

我是C编程的新手,我发现提示用户输入对于像我这样的初学者来说是一个挑战。

我只是编写一个简单的C代码来连续提示用户输入,直到用户输入一个负数以停止程序。在某种程度上,我想要提示用户输入的格式类似于我们在python中所做的操作,其中:

Enter a number: (userinput a number here and press enter)

,输出将是:

The number you entered is 10 (for example)

由于没有输入负数,它尚未停止循环并提示输入另一个数字。

Enter a number: (userinput another number which is negative)

输出:

The number you entered is -10 (for example)

程序停止。

我尝试用C语言编写

#include <stdio.h>

int value = 0;
while (value >= 0) {
    do {
       printf("Enter a number: ");
       scanf("%d",&value);
       printf("The number you entered is %d", &value);

    }

但是我似乎无法在运行程序时首先显示“输入数字:”语句,因为它立即开始时要求用户输入,而没有显示提示消息,直到我输入整数时,提示消息显示与我想要的相反。希望对此有所帮助。

1 个答案:

答案 0 :(得分:6)

新C程序员面临的最大问题之一是正确处理用户输入,尤其是在使用scanf系列函数时。为什么?使用scanf 时,如果出现匹配失败,您必须考虑输入缓冲区(此处为stdin)中剩余的所有字符。为什么?发生匹配失败时,scanf会在失败时停止处理字符,并且导致失败的字符保留在输入缓冲区中未读 ,只是在下次尝试输入内容时咬你。

使问题更复杂的是,scanf转换说明符的区别是如何处理空白的。您的数字输入说明符和%s将占用前导空格,而其余的转换说明符则不会。这意味着,如果您输入的不是数字或%s转换说明符,则必须先考虑并删除结尾的'\n',然后再尝试下一个字符或 character class 转换。

也就是说,每当您使用scanf时,都由您检查退货,以便您确定用户是否使用手动生成的EOF取消输入是 matching 还是 input 失败。

您必须至少检查收益并处理任何表明少于预期转化次数的收益。对于您的情况,如果一次转换为int,则必须在使用1之前检查返回值是否为value。否则,您可以轻松调用未定义行为。现在,仅检查是否发生了所有转换,就无法区分EOF匹配失败-使您失去继续进行最佳操作所需的信息就是退出无效输入,例如

#include <stdio.h>

int main (void) {

    int value = 0;

    while (value >= 0) {
        printf("Enter a number: ");
        if (scanf("%d",&value) != 1) {
            fputs ("error: invalid input\n", stderr);
            return 1;
        }
        printf("The number you entered is %d\n", value);
    }

    return 0;
}

使用/输出示例

$ ./bin/scanfpos2
Enter a number: 1
The number you entered is 1
Enter a number: 10
The number you entered is 10
Enter a number: foo
error: invalid input

注意:,在输入无效整数时,程序只能执行结束操作,您不知道输入是否已取消或是否发生 matching 失败如果失败是 matching 失败,则可以通过清空输入缓冲区来采取适当的措施以继续操作。)

scanf处理输入的正确方法是覆盖所有潜在的错误情况,并通过清除有问题的字符的输入缓冲区来优雅地响应 matching 失败,从而使您可以继续输入。使用小的辅助函数来清除stdin会有所帮助,而不必在代码中每使用scanf的地方重复包含一个清除循环。一个简短的例子是:

/** remove all characters that remain in stdin */
void empty_stdin (void)
{
    int c = getchar();

    while (c != '\n' && c != EOF)
        c = getchar();
}

仅从stdin读取所有字符,直到遇到'\n'EOF。将其与对scanf返回的扩展检查相结合,将使您能够优雅地处理 matching 失败,例如

#include <stdio.h>

/** remove all characters that remain in stdin */
void empty_stdin (void)
{
    int c = getchar();

    while (c != '\n' && c != EOF)
        c = getchar();
}

int main (void) {

    int value = 0;

    while (value >= 0) {
        int rtn;    /* variable to store return on scanf */

        printf ("Enter a number: ");
        rtn = scanf ("%d",&value);
        if (rtn == EOF) {   /* handle EOF */
            fputs ("user canceled input.\n", stderr);
            break;
        }
        else if (rtn == 0) {    /* handle matching/input failure */
            fputs ("error: invalid input\n", stderr);
            empty_stdin();
        }
        else    /* good input - output value */
            printf("The number you entered is %d\n", value);
    }

    return 0;
}

使用/输出示例

$ ./bin/scanfpos2
Enter a number: 1
The number you entered is 1
Enter a number: 10
The number you entered is 10
Enter a number: foo
error: invalid input
Enter a number: -1
The number you entered is -1

如果输入了非整数,例如foo,则会捕获该字符,并从stdin中删除字符,然后循环再次提示输入。

仔细研究。 C不是python。 Python会向您隐藏许多实现细节,从而从本质上保护您免受实例侵害,但是它也有缺点。使用C,没有任何东西对您隐藏。您可以自由控制自己不拥有的内存的写入,在转换失败后反复尝试从输入缓冲区读取数据,等等。您负责这些细节。

最后,所有这些都是建议为新用户使用fgets或POSIX getline进行输入的主要原因。有了足够大小的缓冲区(不要略过),fgets将一次从输入缓冲区读取一行,以防止冒犯性的字符仅在等待再次咬住您时出现。 getline会分配足够大小的缓冲区,无论该行有多长-但您有责任在完成处理后释放内存。研究这两种方法,以代替使用scanf。读取行后,您随时可以在保存行的缓冲区上调用sscanf来解析其中的数值。