c中如何通过用户输入使用fgets()来控制while循环的执行?

How to use fgets() to control the execution of while loop through user input in c?

我正在尝试用 c 编写一个程序,其中我可以通过来自标准输入的用户输入来控制 while 循环的执行。我已经通过 scanf 和 getchar 函数成功完成了。现在我正在尝试使用广泛推荐使用的 fgets() 函数而不是 scanf() 函数来复制它。我写了下面的代码:

#include<stdio.h>
#include<string.h>

int main()
{
 char loop[4]= "yes";

 printf("%s\n",loop);
 while(strcmp(loop,"yes")==0)
    {
     printf("Do you want to continue? [yes|no]: ");
     fgets(loop,4,stdin);
    }
}

在终端的输出中,我得到以下内容:

Do you want to continue? [yes|no]: yes
Do you want to continue? [yes|no]: 

我收到继续循环的提示,当我键入 'no' 时,它会停止,但只要我键入 'yes',循环就会执行一次然后停止。

我猜问题出在我一按下回车键,fgets() 就将其存储到循环变量中,这就是 while 循环终止的原因。我在想正确的方向吗?如果是的话,在这种情况下,我怎样才能去掉这个额外的字符"Enter"。

改变这个:

fgets(loop, 4, stdin);

对此:

fgets(loop, 5, stdin);

之后,当然,将缓冲区的大小设置为 5,像这样 char loop[5]= "yes";,为了存储单词 "yes" 的 3 个字符,换行符 fgets()将读取(因为它正在读取行,对吗?),以及字符串 NULL 终止符(如您所知)。


要解释代码的行为,您必须理解@DavidBowling 的评论:

Note that by using fgets(loop, 4, stdin);, the newline is left in the input stream when "yes" is entered, so the next input call picks up this newline character.

使用这个程序来证明:

#include<stdio.h>
#include<string.h>

int main()
{
  char loop[4]= "yes";

  while(strcmp(loop,"yes")==0)
  {
    printf("Do you want to continue? [yes|no]: ");
    fgets(loop, 4, stdin);
    printf("|%s|\n",loop);    // I surrounded the output, in order to catch the newline
  }
  return 0;
}

输出:

Do you want to continue? [yes|no]: yes
|yes|
Do you want to continue? [yes|no]: |
|

这表明换行符留在标准输入 (STDIN) 缓冲区中,并在第二次调用 fgets() 时被消耗掉。来自方法的参考:

Reads characters from stream and stores them as a C string into str until (num-1) characters have been read or either a newline or the end-of-file is reached, whichever happens first.

A terminating null character is automatically appended after the characters copied to str.

让我们一步步看看会发生什么:

您键入 yes,然后按 Enter,这会使标准输入缓冲区看起来像这样:

------------------
| y | e | s | \n |
------------------

现在对 fgets(loop, 4, stdin); 的第一次调用被执行,这意味着该方法将尝试从 STDIN 读取 3 (4 - 1) 个字符。它读取 yes,将它们从 STDIN 的缓冲区移动到您声明的程序缓冲区,命名为 loop,然后附加字符串 NULL 终止符。

现在 STDIN 缓冲区是:

------------------
| \n |   |   |   |
------------------

并且在用户有机会输入之前,fgets(loop, 4, stdin); 将被执行(这是方法的职责,因为它们是在 STDIN 的缓冲区中等待被消耗的数据 - 如果没有数据,然后该方法将耐心等待用户输入内容...)。

现在,它将换行符复制到 loop,然后停在那里,因为现在 STDIN 缓冲区为空,并最终将字符串 NULL 终止符附加到 loop(在索引 1 处)。

现在 STDIN 缓冲区为空:

-----------------
|   |   |   |   |
-----------------

因为用户不再输入 - 代码流在 while 循环之后移动,因为循环的条件评估为 false,此时 loop 是一个以换行符作为其第一个字符的字符串。


PS: Removing trailing newline character from fgets() input.