在 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 提供的缓冲区大小,则此方法会产生问题。即使在上面的程序中,如果用户输入 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 常见问题解答的建议相反!而且我绝对不认为自己比 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 的防弹调用到一定程度,但说实话,您自己进行解析最终不会那么麻烦。