fgets不会等待键盘输入。

4
我想从用户的键盘输入中读取两个字符串,这是我尝试的代码:
char nomFichier[50], emp[100], empEtNomFichier[150];
printf("\nDonner le nom du fichier : ");
fgets(nomFichier, sizeof nomFichier, stdin);
printf("\nDonner l'emplacement du fichier : ");
fgets(emp, sizeof emp, stdin)
sprintf(empEtNomFichier, "%s/%s", emp, nomFichier);

问题在于当我运行这段代码时,程序不会等待第一个fgets()的键盘输入,这是程序的外观:

Donner le nom du fichier : 
Donner l'emplacement du fichier : /home/ee/Desktop
/home/ee/Desktop

你能否提供一个最短的可编译示例,以展示问题所在? - Crowman
@PaulGriffiths,请看一下我对我的帖子所做的修改。 - Croviajo
“可编译”是指将其包含在main()函数中,#include正确的头文件等,可以直接复制和编译,并且在编译和运行时产生问题。您当前代码中可能还有其他原因导致问题,创建一个可编译的示例将有助于消除这种可能性。例如,如果我只是将您的代码放入main()函数中,我无法重现您的问题。 - Crowman
4个回答

7

问题在于fgets()函数会将终止符'\n'读入字符串中。除非缓冲区已满或者遇到EOF但没有换行符,否则返回的字符串中最后一个字符将是'\n'。

解决方法是:

n = strlen(nomFichier);
if (n>0 && nomFichier[n-1]=='\n') { nomFichier[n-1] = 0; }

...并在if块中对emp字符串执行相同的操作。还有一些方法可以使用'\n'作为分隔符使用strtok,或者使用sscanf格式为"%xx[^\n]%*c",我相信其他人会有不同的解决方案。("xx"是该scanf格式中的最大字符串长度,以十进制表示。)

顺便说一句,无法通过fflush来解决另一个问题,因为fflush不一定会执行任何操作。例如,GNU C实现将fflush(stdin)视为无操作。

问题很可能是由先前的scanf()读取了一个值,但没有读取换行符导致的。在令牌导向输入(scanf/fscanf)和行导向输入(fgets)之间切换的程序必须准备好处理剩余的行。

最快的解决方法是:

scanf("%*[^\n]%*c"); /* discard up to and including the next '\n' character */

...或者更直接一些:

int c;
while ( (c = getchar()) != EOF && c != '\n') { }

无论如何,我会将其放入静态函数中,并让优化器决定是否内联它。 每次从scanf()切换到fgets()输入时都需要这样做。

谢谢给我点踩的人。这不仅提醒了我要回来处理这个问题,还让我的声望从回文中解脱出来。 - Mike Housky

0

其他答案都对我没用。我是通过以下方式得到了我想要的行为:

只需创建一个丢弃变量并将换行符读入其中,然后再进行 fgets()

// . . . code above that leaves a \n in the stdin
char str[20];
char c;
printf("Prompt: ");
scanf("%c", &c);
fgets(str, 20, stdin);
printf("str = %s\n", str);

然而,正如下面的评论指出的那样,这并不是一个干净的“修复”。

2
代码如何知道 scanf("%c", &c); 读取的是 换行符 而不是其他字符?之前的读取可能因各种原因而停止,而不一定在 '\n' 处停止。此外,假设在 printf("Prompt: "); 时没有待处理的输入,则如果输入仅为 "\n",则 scanf("%c", &c); 会读取用于 fgets(str, 20, stdin); 的换行符。 - chux - Reinstate Monica
@chux-ReinstateMonica 是的,我完全同意XD。我的答案不是解决方案,而是一个临时的hack。我会回去编辑它,以使其更清晰明了。我只是想分享一些可以帮助那些需要在提交学校作业之前快速修补这个奇怪问题的人的东西。 - Ac Hybl
如果您担心删除非换行符的字符,那么在删除之前不可以先进行检查吗? - Ac Hybl
1
“在删除之前,你能不能检查一下?” --> 是的。 在我看来,与其消耗上一个输入函数(即 '\n'),不如让上一个输入函数读取整行。使用 scanf()可能 的,但更好的方法是避免使用 scanf(),而是使用 fgets() - chux - Reinstate Monica

0

尝试:

char nomFichier[50], emp[100], empEtNomFichier[150];
printf("\nDonner le nom du fichier : ");
fflush(stdout);
fgets(nomFichier, sizeof nomFichier, stdin);
printf("\nDonner l'emplacement du fichier : ");
fflush(stdout);
fgets(emp, sizeof emp, stdin)
sprintf(empEtNomFichier, "%s/%s", emp, nomFichier);

stdout 是行缓冲的,因此如果没有 \n 字符,它将不会输出 - 尝试使用 fflush();

或者尝试:

char buf[256]={0x0};
setvbuf(stdout, buf, _IONBF, 256);

我一开始也是这么想的,但他的问题是输出的东西太多了,而不是太少。无论如何,他说他尝试过这个方法,但没有成功。 - Crowman
好的 - 我错过了他尝试使用fflush()的地方。但那是一种解决方案。另一种是使用setvbuf()将stdout更改为无缓冲区 - 不是一个好主意。我会添加它。 - jim mcnamara
是的,我删除了那个答案和评论。我不确定他是否在代码中做了其他事情,而这些事情可能会影响缓冲。 - Crowman

0

我认为这是解决问题的最简单方法,而且对于新手来说易于理解。做两次 :)

#include<stdio.h>
int main(){
    char ch[50];
    char c;
    printf("Enter a random character: ");
    scanf("%c", &c);
    printf("Enter your name: ");
    fgets(ch, sizeof(ch), stdin); // If you do this 1 time, it will not wait for your input
    fgets(ch, sizeof(ch), stdin);
    puts(ch);
}

1
代码片段在问题中显示的是正确的,绝对没有任何理由不正确。问题显然是其他地方引起的。 - David C. Rankin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接