如何处理C中的交互式输入/输出?

时间:2017-07-10 17:38:25

标签: c design-patterns io interactive

根据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提供的缓冲区大小,则此方法会产生问题。即使在上述程序中,如果用户输入asdfasdf14,也会出现问题。

在这种情况下,理想情况下,我们应该忽略所有字符,直到我们看到'\ 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中交互式输入/输出的典型处理如何?

1 个答案:

答案 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 中的交互式输入定义了痛苦的屁股。你真的必须跳过所有这些篮球以防止输入错误。