在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  int value = 0; while (value >= 0) { do { printf("Enter a number: "); scanf("%d",&value); printf("The number you entered is %d", &value); } 

但我似乎无法在运行程序时首先显示“输入数字:”语句,因为它在启动时立即请求用户输入而不显示提示消息,直到我输入整数时,提示消息显示这与我想要的相反。 希望得到一些帮助。

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

使问题进一步复杂化的是,差异scanf 转换说明符如何处理空格。 您的数字输入说明符和%s将使用前导空格,而其余的转换说明符则不会。 这意味着如果您使用除数字或%s转换说明符之外的输入 – 您必须在尝试下一个字符或字符类转换之前考虑并删除尾随的'\n'

也就是说,每当您使用scanf ,由您来检查返回,以便您可以确定用户是否通过手动生成的EOF来确定是否发生了匹配输入故障,从而取消了输入。

在最低限度,您必须检查返回并处理任何返回,表明发生的预期转换次数少于此数。 在您的情况下,只需转换为int ,您必须在使用value之前检查返回值是否为1 。 否则,您可以轻松调用未定义的行为 。 现在只检查是否所有转换都不允许您区分EOF匹配失败 – 让您没有继续做最好的信息,您可以做的是退出无效输入,例如

 #include  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 

注意:在输入无效的整数时,所有程序都可以完成,你不知道输入是否被取消或者是否发生了匹配的失败,这将允许你通过清空输入缓冲区继续采取适当的操作失败是一个匹配的失败。)

使用scanf处理输入的正确方法是覆盖所有潜在的错误条件,并通过清除有问题字符的输入缓冲区来优雅地响应匹配的故障,从而允许您继续输入。 有一个小帮助函数可以清除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返回的扩展检查相结合将允许您优雅地处理匹配的故障,例如

 #include  /** 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来解析它的数值。