欢迎使用scanf 进行用户输入的陷阱。 fgets 或 POSIX getline 是更好的选择,因为它们避免了 scanf 固有的许多(“输入缓冲区中还剩下什么?”)问题。话虽如此,重要的是要知道如何正确使用scanf。
使用scanf 只不过是一个练习(1)实际读取了哪些字符? (2) 输入缓冲区中有哪些字符未读?为了处理第 2 个问题,您需要一些方法来清空在调用 scanf 之间保留在输入缓冲区中的任何字符,以确保您不会被任何剩余的字符咬伤。您还必须知道至少保留一个字符,否则您将简单地阻止等待输入清除。清空stdin 的标准方法是循环遍历剩余的任何字符,直到您读取结尾的"\n'(用户按“Enter”的结果)或EOF。例如,
void empty_stdin (void)
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
(您也可以将其编写为单个 for 语句,例如 for (int c = getchar(); c != '\n' && c != EOF; c = getchar()) {},但 while 通常更具可读性。)
这不仅仅是正确处理输入的问题。由于您想强制用户输入“是”或“否”,因此一般的方法是不断循环,直到满足输入条件。你在正确的轨道上,但没有完全到达那里。相反,您可以这样做:
char buffer[1024] = ""; /* don't skimp on buffer size */
for (;;) { /* loop continually */
int rtn; /* value to hold return from scanf */
printf ("Do you want to continue (yes/no): "); /* prompt */
rtn = scanf ("%1023[^\n]", buffer); /* read input */
if (rtn == EOF) { /* user canceled input, bail */
fprintf (stderr, "user canceled input.\n");
exit (EXIT_FAILURE);
}
else if (rtn == 1) { /* value stored in buffer */
/* check for input meeting criteria */
if (strcmp (buffer, "yes") == 0 || strcmp (buffer, "no") == 0) {
empty_stdin(); /* don't leave characters in stdin */
break; /* success, break input loop */
}
}
/* handle wrong input */
empty_stdin(); /* don't leave characters in stdin */
fprintf (stderr, "error: invalid input.\n\n");
}
如果您以这种方式进行读取,您可以控制输入缓冲区中剩余的内容,不仅针对循环中的每次读取,而且确保您离开时用户输入的缓冲区中没有字符。
如上所述,正确使用 scanf 只是一个会计问题,一旦您知道每个 格式说明符 的行为方式,以及 输入 或 匹配的效果 failures 在失败点停止读取,您可以回答以下问题: (1) 实际读取了哪些字符? (2) 输入缓冲区中还有哪些字符未读?
一个简短的例子可能会有所帮助:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main (void) {
char buffer[1024] = ""; /* don't skimp on buffer size */
for (;;) { /* process loop - doing stuff */
printf ("\nprogram processing....\n\n");
for (;;) { /* input - loop continually */
int rtn; /* value to hold return from scanf */
printf ("Do you want to continue (yes/no): "); /* prompt */
rtn = scanf ("%1023[^\n]", buffer); /* read input */
if (rtn == EOF) { /* user canceled input, bail */
fprintf (stderr, "user canceled input.\n");
exit (EXIT_FAILURE);
}
else if (rtn == 1) { /* value stored in buffer */
/* check for input meeting criteria */
if (strcmp (buffer, "yes") == 0) {
empty_stdin(); /* don't leave characters in stdin */
break; /* success, break input loop */
}
else if (strcmp (buffer, "no") == 0)
goto alldone;
}
/* handle wrong input */
empty_stdin(); /* don't leave characters in stdin */
fprintf (stderr, "error: invalid input.\n\n");
}
}
alldone:;
printf ("that's all folks...\n");
}
(注意:上方的"yes" 和"no" 响应分别进行比较,以便在"yes" 或exit 上继续处理"no")
使用/输出示例
$ ./bin/scanf_yes_no
program processing....
Do you want to continue (yes/no): what?
error: invalid input.
Do you want to continue (yes/no): obnoxious long line because cat stepped on kbd
error: invalid input.
Do you want to continue (yes/no): yes
program processing....
Do you want to continue (yes/no): yes
program processing....
Do you want to continue (yes/no):
error: invalid input.
Do you want to continue (yes/no): no
that's all folks...