根据C FAQ:http://c-faq.com/stdio/scanfprobs.html
我们不应该使用scanf
作为交互式输入输出,而是应该使用fgets
读取整行,然后尝试用sscanf
解析它,提示用户如果sscanf
返回解析错误,则再次输入。
这个,IIUC,会产生这样的代码:
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
int main()
{
char inpbuff[5];
signed char n;
bool read_correctly = false;
while(!read_correctly) {
printf("Please enter an 8bit number: ");
if(fgets(inpbuff, sizeof(inpbuff), stdin) == NULL)
return EXIT_FAILURE;
if(sscanf(inpbuff, "%hhd", &n) == 1)
read_correctly = true;
}
printf("You have entered: %hhd\n", n);
return EXIT_SUCCESS;
}
对我来说,如果用户键入的行超过为fgets
提供的缓冲区大小,则此方法会产生问题。即使在上述程序中,如果用户输入asdf
或asdf14
,也会出现问题。
在这种情况下,理想情况下,我们应该忽略所有字符,直到我们看到'\ n',忽略此\n
,然后再次要求用户提供输入。这将导致这样的方法:
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
int main()
{
char inpbuff[5];
signed char n;
bool read_correctly = false;
while(!read_correctly) {
printf("Please enter an 8bit number: ");
switch(scanf("%hhd", &n)) {
case 1:
read_correctly = true;
break;
case 0: {
char sink;
do {
sink = fgetc(stdin);
if(sink == EOF)
return EXIT_FAILURE;
} while(sink != '\n');
break;
}
default:
return EXIT_FAILURE;
}
}
printf("You have entered: %hhd\n", n);
return EXIT_SUCCESS;
}
我认为它必须是次优的,因为它与C FAQ建议的相反!我绝对不认为自己比C FAQ作者更聪明。
那么,C中交互式输入/输出的典型处理如何?
答案 0 :(得分:5)
您的版本错过了一个极端情况 - 假设我输入了1r4
。您的scanf
调用将成功转换并将1
分配给n
,返回1表示成功,并在输入流中保留r4
以填充下一个读取。理想情况下,您想完全拒绝1r4
。
这就是为什么建议将输入作为文本读取,然后处理该缓冲区。如果有人输入的行超过缓冲区的大小,你可以在输入阶段通过检查缓冲区中的换行来处理它 - 如果它不存在,则拒绝输入太大,然后读取并丢弃任何其他字符,直到你看到换行符。
while ( fgets( buffer, sizeof buffer, stdin ) )
{
char *newline = strchr( buffer, '\n' );
if ( !newline )
{
/**
* input too large for buffer, throw away current input, read and
* discard additional characters until we see the newline or EOF
*/
for ( int c = getchar(); c != EOF && c != '\n'; c = getchar() )
;
}
else
{
// process input buffer
}
}
是的,在1分钟的痛苦中,C 中的交互式输入定义了痛苦的屁股。你真的必须跳过所有这些篮球以防止输入错误。