fgets 在换行前设置空字符后清除输入流

Clear input stream after fgets set null char before newline

我有一个程序,用户可以在其中输入两个输入。由于我无法控制用户输入的内容,因此用户可以超过数组的固定大小。由于 fgets() appends 在空字符之前保留了一个换行符,如果用户超出预期大小时换行符无法容纳,则空字符将截断输入。用户点击回车时的换行符是否仍然存在于输入流中?如果是这样,这就是为什么 fgets() 因为第一次输入的换行而第二次跳过的原因吗?

#include <stdio.h>
int main(){
    char str[5];
    fgets(str,5,stdin);
    printf("Output:%s",str);

    fgets(str,5,stdin);
    printf("Output:%s",str);

    return 0;
}

示例输入 ABCDE\n

输出

Output:ABCDOutput:E

阅读此 SO 答案后 fgets() 不会再次提示用户 ,问题似乎不是通过 fflush(stdin) 刷​​新输入流,但我听说过相互矛盾的信息,因为这会导致未定义的行为。我的最后一个问题是,如果导致问题的是保留的换行符,那么清除输入流的适当方法是什么?

如果 fgets 读取的字符串没有以换行符结尾,您就知道它仍在缓冲区中。在这种情况下,循环调用 getchar 直到换行。

fgets(str,5,stdin);
printf("Output:%s",str);
if (strchr(str, '\n') == NULL) {
    int c; 
    while ((c = getchar()) != EOF && c != '\n');
}

fgets(str,5,stdin);
printf("Output:%s",str);

the user can go past the fixed size of the array. Since fgets() appends a newline to the end before the null character

不,不是。它将从输入中读取的字符写入提供的缓冲区,直到 并包括 第一个换行符,或者直到指定的缓冲区大小用完(字符串终止符少一个字节),或者直到发生错误或到达流的末尾,以先到者为准。换行符不是fgets()发明的;它来自输入。

, in the event that a newline cannot fit when the user goes beyond the intended size, the null character truncates the input. Does the newline character when the user hits enter still exist in the input stream?

所有用户输入但未复制到缓冲区的字符在流中等待读取。如果用户输入一个,那将包括换行符。

If so, is this the reason why fgets()skips the second time because of the newline from the first input?

fgets() 不会跳过,但它会从上一个调用停止的地方开始将字符从输入传输到缓冲区。没有字符丢失。这意味着如果第一次调用没有 return 整个事情,那么第二次调用 return 是第一个输入行的一部分。您需要以一种或另一种方式考虑输入不符合您的行长度预期的可能性。

the issue seems to be not flushing the input stream via fflush(stdin),

不,不是。刷新用于将缓冲的 output 发送到底层输出设备。刷新输入流会产生未定义的行为。原则上,这可以表现为缓冲区转储,并且给定的实现甚至可以指定这样的行为,但你不希望那样,因为那里缓冲的数据可能比您想删除的多。

but I've heard conflicting information saying as this leads to undefined behavior. My last question is, what would be the appropriate way to clear the input stream if it's the retained newline that's causing issues?

您从输入开始阅读,直到您阅读换行符。有很多 I/O 函数可供选择来完成此任务。 fgets() 本身可能会很方便,因为您已经在使用它了:

char str[5];

if (fgets(str, 5, stdin)) {
    printf("Output:%s", str);
    // read and consume the tail of the line, if any (overwrites str)
    while (!strchr(str, '\n') && fgets(str, 5, stdin)) { /* empty */ }
}

fgets() 读到

1) 换行
2) 缓冲区已满
3) 文件结束
4)输入错误(少见)

此代码读取并处理#3 和#4

 #define N 5
 char buf[N];
 if (fgets(buf, sizeof buf, stdin) == NULL) {
   // Handle EOF or Error
   return EOF;
 }

区分是否存在 '\n' ...(#2 来自 #1)

 // look for a lack of \n
 if (strchr(buf, '\n') == NULL) {

如果是,请阅读直到找到或 EOF

   int ch;
   while ((ch = fgetc(stdin)) != '\n' && ch != EOF);
 }

--

请勿使用以下代码。可以通过读取 空字符 作为第一个字符来利用它。

 size_t len = strlen(buf);
 if (buf[len - 1] != '\n') {  // bad way to detect \n

可以使用

 if (len > 0 && buf[len - 1] != '\n') {  // Good

Being that I can't control what the user enters...

不,你不能。

the user can go past the fixed size of the array.

没错。这始终是一个问题。但是,一般来说,您需要安排事情以防止这种情况发生。

例如,如果你真的想限制用户(比如说)一个 4 个字符长的输入字符串,让他输入他想要的任何内容,然后看看他输入了多少,如果超过你的限制,打印一个不错的错误消息或其他东西。但如果您希望输入 4 个字符加上一个换行符,我不建议调用 fgets(str, 5, stdin),因为当(不是如果)用户键入太多时,它很难恢复。

in the event that a newline cannot fit when the user goes beyond the intended size, the null character truncates the input. Does the newline character when the user hits enter still exist in the input stream?

绝对是。

If so, is this the reason why fgets()skips the second time because of the newline from the first input?

几乎是。

我建议分配一个更大的缓冲区,然后进行如下操作:

char inpbuf[512);
if(fgets(inpbuf, sizeof(inpbuf), stdin) == NULL) {
    fprintf(stderr, "end of file\n");
    return;
}
char *p = strrchr(inpbuf, '\n');
if(p == NULL) {
    fprintf(stderr, "looks like you typed *way* too much\n");
    return;
}
*p = '[=10=]';        /* erase the \n */
if(strlen(inpbuf) > 4) {
    fprintf(stderr, "you typed too much (max 4)\n");
    return;
}
strcpy(str, inpbuf);
printf("Output:%s", str);

不过,此代码的一个小故障是:如果用户在按下 Return 之前按下文件结束键(Unix/Linux 上的 control-D),您将错误地得到"looks like you typed way too much" 消息。