函数中fgets()无法工作

3

我正在编写一个程序,使用fgets()扫描一个3个字符长度的字符串作为起飞和到达机场的代码。当我在方法外部编写语句(在主函数中)时,它能正常工作,但在方法内部则无法等待输入并留下变量为空。

void newFlight ()
    {
        printf("\n");
        printf("============= CREATE A NEW FLIGHT ============= \n");
        printf("Type 0 at any point to exit to main menu. \n");
        printf("\n");

        printf("EnterFlight ID (0 to cancel) : ");
        scanf("%d", &flyList[curFly].flightID);

        char codeA [4];
        printf ("Enter Destination (Airport Code): ");
        fgets(codeA, 3, stdin);
        strcpy(codeA, flyList[curFly].arrive);
        printf("%s   %s \n", codeA, flyList[curFly].arrive);  //TEST

        printf("Enter Place Of Departure: ");
        char codeD[4];
        scanf("%s", codeD);
        strcpy(codeD, flyList[curFly].depart);
        printf("%s   %s \n", codeD, flyList[curFly].depart);  //TEST

        printf("Enter Date Of Departure (DD MM YYYY): ");
        scanf("%hd %hd %hd", &flyList[curFly].timeOfDep.day, &flyList[curFly].timeOfDep.month, &flyList[curFly].timeOfDep.year);

        printf("Enter Time Of Departure (HH MM)in 24Hr Format: ");
        scanf("%hd %hd", &flyList[curFly].timeOfDep.hour, &flyList[curFly].timeOfDep.minute);       

        curFly++;
    }

scanf保留换行符。并通过下一个strcpy(codeA, flyList[curFly].depart);进行重写。 - BLUEPIXY
@Barmar 我已经检查过了,我认为这与他遇到的迭代问题不同,我的问题在于实际扫描字符串。 - Matthew Cassar
1
%d 会将换行符留在输入缓冲区中,供下一次输入操作使用;而这个输入操作是 fgets(),它会查找直到下一个换行符的内容。输入的第一个字符是换行符;这也是 fgets() 只读取该字符的原因。这是一个标准问题,@Barmar 确认了其中一个可能的重复问题。 - Jonathan Leffler
1
最后,就我而言(目前为止),您没有检查任何输入操作,因此不知道 fgets() 是否正常工作。 您必须检查每个输入操作以确保它正确工作。 如果您不这样做,那么您将面临灾难。 - Jonathan Leffler
再次检查 strcpy - BLUEPIXY
显示剩余5条评论
2个回答

1
混合使用fgets()scanf()是有问题的。 fgets()会消耗Enter (\n)。
scanf("%d", ...看到了\n,这会停止%d转换,并将\n放回stdin以供下一个IO操作使用 - 这恰好是OP的fgets(),它会迅速返回一个短字符串。
还需要检查scanf()的结果。指定"%s的宽度是好的,比如"%3s
快速解决方案:只使用scanf()
 //  scanf("%d", &flyList[curFly].flightID);
if (1 != scanf("%d", &flyList[curFly].flightID)) handle_error();
 ...
char codeA [4];
// fgets(codeA, 3, stdin);
if (1 != scanf("%3s", codeA) handle_error();
...
// Likely backwards
// strcpy(codeA, flyList[curFly].arrive);
strcpy(flyList[curFly].arrive, codeA);
...
char codeD[4];
// scanf("%3s", codeD);
if (1 != scanf("%3s", codeD)) handle_error();
...
// scanf("%hd %hd %hd", &flyList[curFly].timeOfDep.day, &flyList ...
if (3 != scanf("%hd %hd %hd", &flyList[curFly].timeOfDep.day, &flyList[curFly].timeOfDep.month, &flyList[curFly].timeOfDep.year)) handle_error();
...
// scanf("%hd %hd", &flyList[curFly].timeOfDep.hour, &flyList ...
if (2 != scanf("%hd %hd", &flyList[curFly].timeOfDep.hour, &flyList[curFly].timeOfDep.minute)) handle_error();

更好的解决方案:使用 fgets()/sscanf()
 //  scanf("%d", &flyList[curFly].flightID);
char buf[100];
if (fgets(buf, sizeof buf, stdin) == NULL) Handle_EOForIOError();
if (1 != sscanf(buf, "%d", &flyList[curFly].flightID)) handle_parse_error();
... 
if (fgets(buf, sizeof buf, stdin) == NULL) Handle_EOForIOError();
if (1 != sscanf(buf, "%3s", flyList[curFly].arrive) handle_parse_error();
...
etc.

顺便提一下:scanf()格式"%hd %hd""%hd%hd"的作用是相同的。

谢谢:D,这个完美地解决了问题。 我认为我会使用基本的解决方案,因为这是我在C语言中的第一个任务。你介意解释一下 'if (1 != scanf("%d", &flyList[curFly].flightID)) handle_error();' 这个扫描字符串的部分吗? 再次感谢。 - Matthew Cassar
%d 告诉 scanf() 查找一个 int。如果成功,scanf() 返回 1,因为找到了 1 个指示符。如果返回 0(没有找到 int)或 EOF(文件结束或 IO 错误),则执行函数 handle_error()handle_error() 只是一个虚拟名称,用于说明如何处理非法输入的代码 - 我发现在第二种解决方案中更容易实现。 - chux - Reinstate Monica
这可能有点晚了..但是你能否建议一种解决方法,以避免溢出并且下一个fgets不会自动执行。我的意思是,如果输入字符串超过char数组的大小,下一个char数组将自动分配。我能想到的一个解决方案是使用fseek并将stdin指针移动到末尾。 - Pushan Gupta
@VidorVistrom,你是否在寻找类似于getline()的东西? - chux - Reinstate Monica
@chux 可能是这样。getline 不总是出现在 C 标头中。虽然它是 C++ 的东西,但确实有类似的东西。如果您能使用 fgets 本身提供一个溢出解决方案,那将是很好的。 - Pushan Gupta
@VidorVistrom getline() 不是标准 C 库的一部分,但源代码很容易找到。你想要的不能通过从该库中调用单个函数来完成。fgets() 可以作为构建块工作,但你的请求缺乏细节,并且存在许多问题(内存管理、输入空字符处理、如何传达读取的大小、错误指示、限制大小以防止黑客等)与你提出的 一般 问题有关。建议你在 SO 上搜索,如果需要,创建详细说明你可以做什么的示例代码并发布一个问题。 - chux - Reinstate Monica

1
问题不是来自于 fgets,而是来自于 scanf,因为当执行以下代码行时: scanf("%d", &flyList[curFly].flightID); 并在按下回车键后,换行符仍然留在缓冲区中,所以当调用 fgets 时,它立即接收到该字符,因此表现得好像你按下了 Enter 并继续执行。防止这种情况发生的一种简单方法是在每个 scanf 后面放置一个 getchar,以便它接收换行符。

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