在 C 中处理交互式 input/output 应该是什么样子?
How processing interactive input/output in C should look like?
根据 C 常见问题解答:http://c-faq.com/stdio/scanfprobs.html
我们不应该使用 scanf
进行交互式输入输出,而是应该使用 fgets
读取整行,然后尝试使用 sscanf
解析它,提示如果 sscanf
returns 解析错误,用户需要再次输入。
这,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 常见问题解答的建议相反!而且我绝对不认为自己比 C FAQ 作者更聪明。
那么,C 中交互式 input/output 的典型处理是怎样的?
您的版本遗漏了一个特殊情况 - 假设我输入 1r4
。您的 scanf
调用将成功转换并将 1
分配给 n
,return 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 中的交互式输入定义 极度痛苦。您确实必须跳过所有这些障碍才能防止错误输入。
您 可以 对 scanf
的防弹调用到一定程度,但说实话,您自己进行解析最终不会那么麻烦。
根据 C 常见问题解答:http://c-faq.com/stdio/scanfprobs.html
我们不应该使用 scanf
进行交互式输入输出,而是应该使用 fgets
读取整行,然后尝试使用 sscanf
解析它,提示如果 sscanf
returns 解析错误,用户需要再次输入。
这,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 常见问题解答的建议相反!而且我绝对不认为自己比 C FAQ 作者更聪明。
那么,C 中交互式 input/output 的典型处理是怎样的?
您的版本遗漏了一个特殊情况 - 假设我输入 1r4
。您的 scanf
调用将成功转换并将 1
分配给 n
,return 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 中的交互式输入定义 极度痛苦。您确实必须跳过所有这些障碍才能防止错误输入。
您 可以 对 scanf
的防弹调用到一定程度,但说实话,您自己进行解析最终不会那么麻烦。