简体   繁体   English

fgets()不能在C语言中按预期工作

[英]fgets() doesn't work as expected in C

Given the following code: 给出以下代码:

#include <stdio.h>
int main()
{
 int testcase;
 char arr[30];
 int f,F,m;
 scanf("%d",&testcase);
 while(testcase--)
 {
  printf("Enter the string\n"); 
  fgets(arr,20,stdin);
  printf("Enter a character\n");
  F=getchar();
  while((f=getchar())!=EOF && f!='\n')
    ;
  putchar(F);
  printf("\n");
  printf("Enter a number\n");
  scanf("%d",&m);
 }
 return 0;
}

I want a user to enter a string, a character and a number until the testcase becomes zero. 我希望用户输入一个字符串,一个字符和一个数字,直到测试用例变为零为止。

My doubts / questions: 我的疑问/问题:

1.User is unable to enter a string. 1.用户无法输入字符串。 It seems fgets is not working. 似乎fgets无法正常工作。 Why? 为什么?

2.If i use scanf instead of fgets ,then getchar is not working properly, ie whatever character I input in it just putchar as a new line. 2.如果我使用scanf而不是fgets ,则getchar无法正常工作,即我在其中输入的任何字符都只是putchar作为新行。 Why? 为什么?

Thanks for the help. 谢谢您的帮助。

Mixing functions like fgets() , scanf() , and getchar() is error-prone. 混合函数fgets()scanf()getchar()容易出错。 The scanf() function usually leaves a \\n character behind in the input stream, while fgets() usually does not, meaning that the next call to an I/O function may or may not need to cope with what the previous call has left in the input stream. scanf()函数通常在输入流中留下一个\\n字符,而fgets()通常不这样做,这意味着对I / O函数的下一个调用可能或可能不需要处理上一个调用留下的内容在输入流中。

A better solution is to use one style of I/O function for all user input. 更好的解决方案是对所有用户输入使用一种样式的I / O功能。 fgets() used in conjunction with sscanf() works well for this. sscanf()结合使用的fgets()可以很好地实现此目的。 Return values from functions should be checked, and fgets() returns a null pointer in the event of an error; 应该检查函数的返回值,并且在发生错误的情况下fgets()将返回空指针。 sscanf() returns the number of successful assignments made, which can be used to validate that input is as expected. sscanf()返回成功分配的数量,可用于验证输入是否如预期的那样。

Here is a modified version of the posted code. 这是发布代码的修改版本。 fgets() stores input in a generously allocated buffer; fgets()将输入存储在慷慨分配的缓冲区中; note that this function stores input up to and including the \\n character if there is enough room. 请注意,如果有足够的空间,此函数最多可以存储\\n字符(包括\\n字符)的输入。 If the input string is not expected to contain spaces, sscanf() can be used to extract the string, leaving no need to worry about the newline character; 如果预期输入字符串不包含空格,则可以使用sscanf()提取字符串,而无需担心换行符; similarly, using sscanf() to extract character or numeric input relieves code of the burden of further handling of the \\n . 类似地,使用sscanf()提取字符或数字输入可减轻代码对\\n进一步处理的负担。

#include <stdio.h>

int main(void)
{
    int testcase;
    char arr[30];
    char F;
    int m;
    char buffer[1000];

    do {
        puts("Enter number of test cases:");
        if (fgets(buffer, sizeof buffer, stdin) == NULL) {
            /* handle error */
        }
    } while (sscanf(buffer, "%d", &testcase) != 1 || testcase < 0);

    while(testcase--)
    {
        puts("Enter the string");
        /* if string should not contain spaces... */
        if (fgets(buffer, sizeof buffer, stdin) == NULL) {
            /* handle error */
        }
        sscanf(buffer, "%29s", arr);
        printf("You entered: %s\n", arr);
        putchar('\n');

        puts("Enter a character");
        if (fgets(buffer, sizeof buffer, stdin) == NULL) {
            /* handle error */
        }
        sscanf(buffer, "%c", &F);
        printf("You entered: %c\n", F);
        putchar('\n');

        do {
            puts("Enter a number");
            if (fgets(buffer, sizeof buffer, stdin) == NULL) {
                /* handle error */
            }
        } while (sscanf(buffer, "%d", &m) != 1);

        printf("You entered: %d\n", m);
        putchar('\n');
    }

    return 0;
}

On the other hand, if the input string may contain spaces, fgets() can read input directly into arr , but then the stored string will contain a \\n character, which should probably be removed. 另一方面,如果输入字符串可能包含空格,则fgets()可以将输入直接读取到arr ,但是存储的字符串将包含\\n字符,应该将其删除。 One way of doing this is to use the strcspn() function to find the index of the \\n : 一种方法是使用strcspn()函数找到\\n的索引:

#include <string.h>  // for strcspn()
/* ... */
        puts("Enter the string");
        /* or, if string may contain spaces */
        if (fgets(arr, sizeof arr, stdin) == NULL) {
            /* handle error */
        }
        /* replace newline */
        arr[strcspn(arr, "\r\n")] = '\0';

        printf("You entered: %s\n", arr);
        putchar('\n');
/* ... */

Note that a maximum width should always be specified when using %s with the scanf() functions to avoid buffer overflow. 请注意,在将%sscanf()函数一起使用时,应始终指定最大宽度,以避免缓冲区溢出。 Here, it is %29s when reading into arr , since arr can hold 30 char s, and space must be reserved for the null terminator ( \\0 ). 在这里,当读入arr时它是%29s ,因为arr可以容纳30个char ,并且必须为空终止符( \\0 )保留空间。 Return values from sscanf() are checked to see if user input is invalid, in which case the input is asked for again. 检查sscanf()返回值以查看用户输入是否无效,在这种情况下,将再次要求输入。 If the number of test cases is less than 0, input must be entered again. 如果测试用例的数量小于0,则必须再次输入。

A simple hack is to write a function to interpret the newline character. 一个简单的技巧是编写一个函数来解释换行符。 Call clear() after each scanf's 在每个scanf之后调用clear()

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

Refer to this question for further explaination: C: Multiple scanf's, when I enter in a value for one scanf it skips the second scanf 请参阅此问题以获取进一步的说明: C:多个scanf,当我为一个scanf输入一个值时,它将跳过第二个scanf

Finally got the solution how can we use scanf and fgets together safely. 最终得到了解决方案,我们如何才能安全地同时使用scanf和fgets。

#include <stdio.h>
int main()
{
 int testcase,f,F,m;
 char arr[30];
 scanf("%d",&testcase);
 while((f=getchar())!=EOF && f!='\n')
  ;
 while(testcase--)
 {
  printf("Enter the string\n");
  fgets(arr,30,stdin);  
  printf("Enter a character\n");
  F=getchar();
  while((f=getchar())!=EOF && f!='\n')
    ;
  putchar(F);
  printf("\n");
  printf("Enter a number\n");
  scanf("%d",&m);
  while((f=getchar())!=EOF && f!='\n')
  ;
 }
}

We need to make sure that before fgets read anything,flushout the buffer with simple while loop. 我们需要确保在fgets读取任何内容之前,请使用简单的while循环清空缓冲区。

Thanks to all for the help. 感谢所有人的帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM