我是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);
}
但是我似乎无法在运行程序时首先显示“输入数字:”语句,因为它立即开始时要求用户输入,而没有显示提示消息,直到我输入整数时,提示消息显示与我想要的相反。希望对此有所帮助。
答案 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
来解析其中的数值。