为什么下面的代码不允许我使用fgets获取用户输入,却可以使用scanf?

为什么下面的代码不允许我使用fgets获取用户输入,却可以使用scanf?,c,scanf,user-input,fgets,C,Scanf,User Input,Fgets,这是一个更大的项目的简短摘录,但该项目的其余部分是无关的,因为我认为我能够孤立这个问题。我怀疑这与我使用FGET的方式有关。我已经读到使用FGET优于scanf,但我似乎无法让它在这里正常工作。当我使用以下代码时,程序不会给我输入数字的机会(只是跳过while循环,检查输入的数字是否在正确的范围内): 正如你所看到的,它从不让我输入数字,而是简单地进入下一步,似乎假设我没有输入任何东西 如果我按以下方式更改代码,一切都会按计划进行: #include <stdlib.h> #def

这是一个更大的项目的简短摘录,但该项目的其余部分是无关的,因为我认为我能够孤立这个问题。我怀疑这与我使用FGET的方式有关。我已经读到使用FGET优于scanf,但我似乎无法让它在这里正常工作。当我使用以下代码时,程序不会给我输入数字的机会(只是跳过while循环,检查输入的数字是否在正确的范围内):

正如你所看到的,它从不让我输入数字,而是简单地进入下一步,似乎假设我没有输入任何东西

如果我按以下方式更改代码,一切都会按计划进行:

#include <stdlib.h>

#define SIZE 10

int main(void)
{
    // ask user for how many items to store
    printf("how many words would you like to enter? (1-%i): ", SIZE);

    // save number of words user would like to store
    char *input = malloc(sizeof(char));
    // fgets(input, 1, stdin);
    scanf("%c", input);

    int words = atoi(input);

    printf("the number of words is: %i\n", words);
    while (words < 1 || words > SIZE)
    {
        printf("please enter a number between 1 and %i: ", SIZE);
        scanf("%i", &words);
    }
}
当我运行该程序时,它再次没有给我机会在应该输入的时候输入:

please enter string #0: you've entered: 
word length: 1

编辑#2:

在研究了David的答案并参考了其他人的评论和其他SO线程之后,我提出了以下版本的代码,它首先要求用户输入他们想要输入的单词数(并验证输入),然后要求用户输入这些单词(再次验证输入)。它似乎在编译w/o错误和警告并正常运行,尽管我不能100%确定我已经测试了所有可能出现用户输入错误的地方,还有一些代码我仍然不完全理解(我将在下面列出)--如果有人有时间/愿望/耐心看一下,并告诉我是否还能改进,请告诉我。我的目标是在另一个程序中使用此代码,该程序将请求用户输入并将条目存储在哈希表中

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

#define BUF_SIZE_WORDS 4096
#define BUF_SIZE_NUMBERS 256
#define MAX_WORDS 10

int word_input(int num_words);

void empty_stdin();

int main(void)
{
    int num_words = 0,       /* number of words to enter */
        word_count_check = 0;          /* word count */

    char buffer[BUF_SIZE_NUMBERS] = "";    /* buffer of sufficient size for input */

    for (;;) /* loop continually until valid input of NUMBER OF WORDS USER WANTS TO ENTER or user cancels */
    {
        printf ("how many words would you like to enter? [1-%d]: ", MAX_WORDS);
        // check for cancellation of input
        if (!fgets (buffer, BUF_SIZE_NUMBERS, stdin))
        {
            fputs ("user canceled input\n", stderr);
            return 1;
        }

        // check if user simply hit enter w/o typing anything
        if(buffer[0] == '\n')
        {
            printf("please enter a value\n");
            continue;
        }


        size_t inlength = strlen(buffer);

        // validate length < BUF_SIZE_NUMBERS - 1
        if (inlength >= BUF_SIZE_NUMBERS - 1)
        {
            fputs ("input exceeds allocated buffer size\n", stderr);
            return 2;
        }

        if (inlength && buffer[inlength - 1] == '\n')
        {
            // printf("hurray!\n");
            buffer[--inlength] = 0;
        }
        else if (inlength == BUF_SIZE_NUMBERS - 1) /* the line was too long */
        {
            printf("you've entered too many characters... please stick to a maximum of %i\n", BUF_SIZE_NUMBERS);
            empty_stdin();
            continue;
        }

        // make sure user actually entered a proper int
        if (sscanf (buffer, "%d", &num_words) != 1) /* sscanf is used for conversion */
        {
            fputs ("invalid conversion to int; please provide valid input\n", stderr);
            continue;
        }

        // check if the number entered is out of range
        if (num_words < 1 || num_words > MAX_WORDS)
            fprintf (stderr, "%2d out of valid range.\n", num_words);
        else
            break; /*if the input has been validated, we can now break out of the for loop */
    }

    // call the word_input function and store its return value in word_count_check
    word_count_check = word_input(num_words);

    // check if the number of words processed equals to the number requested by the user
    if(word_count_check == num_words)
    {
        printf("success!\n");
    }
    else
    {
        printf("something went wrong, since word_count_check != num_words...\n");
    }

}

int word_input(int num_words)
{
    int word_count = 0;

    for(;;) /* loop until word_count == num_words is achieved */
    {
        // declare an array for storing input string
        char buffer[BUF_SIZE_WORDS];
        char valid_input[BUF_SIZE_WORDS];

        // prompt user for input
        printf("please enter a string: ");

        // get input and check for CTRL+D
        if (!fgets(buffer, BUF_SIZE_WORDS, stdin))
        {
            fputs ("user canceled input\n", stderr);
            exit(1);
        }

         // check if user simply hit enter w/o typing anything
        if(buffer[0] == '\n')
        {
            printf("please enter a word that's more than 0 characters\n");
            // empty_stdin();
            continue;
        }

        size_t inlength = strlen(buffer);

        // check if user input exceed buffer size
        if (inlength >= BUF_SIZE_WORDS - 1)
        {
            empty_stdin();
            fputs ("input exceeds allocated buffer size, please try again\n", stderr);
            continue;
        }

        // check if the user entered too many characters
        if (inlength == BUF_SIZE_WORDS - 1) /* the line was too long */
        {
            printf("you've entered too many characters... please stick to a maximum of %i\n", BUF_SIZE_WORDS);
            empty_stdin();
            continue;
        }

        if (inlength && buffer[inlength - 1] == '\n')
        {
            buffer[--inlength] = 0;

            // get rid of trailing spaces using sscanf
            sscanf(buffer, "%s", valid_input);

            // figure out the length of the word the user entered
            int word_length = ((int) strlen(valid_input));
            printf("string length: %i\n", word_length);

            // print out the word entered by the user one character at a time
            printf("you've entered: ");
            for (int i = 0; i < word_length; i++)
            {
                printf("%c", valid_input[i]);
            }
            printf("\n");

            // increment word count
            word_count++;
            printf("word_count = %i\n", word_count);

            if (word_count == num_words)
            {
                return word_count;
            }
        }
    }
}

/* helper function to remove any chars left in input buffer */
void empty_stdin()
{
    int c = getchar();
    while (c != '\n' && c != EOF)
        c = getchar();
}
---这只是检查用户是否手动输入EOF(使用ctrl+d),还是检查其他内容

2) 调用下面的空_stdin()函数似乎会导致某种奇怪的挂起,看起来程序需要我进一步输入,而不是继续下一步,特别是当我经常使用它时(我想为什么不在用户每次输入奇怪的内容时清除输入流?)和/或当我将缓冲区减小到非常小的值,然后故意输入太多字符时

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

3) 最后,我想使用一些代码从文本文件(而不是用户输入)加载字典,并将其存储在哈希表中,在另一个版本中,存储在trie中。除了使用isalpha()确保只存储包含字母的单词外,在处理输入时,除了上面的检查外,是否还需要进行其他检查/验证?是否应该跳过上面的任何检查?

在C中处理字符串没有什么神奇之处——但您确实需要戴上会计帽。。。为什么?在处理输入时,您不仅要考虑放入缓冲区(或存储输入的任何位置)的字符数,还必须考虑输入流中保留的字符数

当使用任何
scanf
函数系列进行输入时,尤其如此。为什么?因为在匹配或输入失败时,处理(读取和删除)输入缓冲区中的字符(
stdin
此处)停止,不再读取更多字符,导致匹配失败的任何字符在输入流中保持未读,只是等着下次尝试阅读时再咬你一口

一些转换说明符使用前导空格(例如,
空格、制表符、换行符、
)而其他转换说明符不使用,这让新的C程序员更加困惑。您的数字转换说明符(以及
“%s”
)使用前导空格,而
“%c”
“%[…]”
不使用前导空格

所有这些都是鼓励新的C程序员使用面向行的输入函数,如
fgets
或POSIX
getline
来处理用户输入的主要原因(因为他们一次读取整个行,包括测试
'\n'
)释放新程序员,使其能够在匹配失败的情况下解释未转换的结尾空白或有问题的字符

使用
fgets
后接
sscanf
提供了允许单独验证(1)输入读取的额外好处;(2)将输入解析并转换为所需的值

(注意:面向行的输入函数唯一需要注意的是,它们读取并在它们填充的缓冲区中包含尾随的
'\n'
,因此您需要根据需要“修剪”尾随的空格。您不希望散乱的
'\n'
字符挂在存储的字符串末尾。)

也就是说,有时使用
scanf
函数族读取输入是有意义的。这样做没有什么错,只要您每次都验证返回并处理所有三种可能的条件:

  • 用户在Linux上按ctrl+d生成手动
    EOF
    (在windoze上按ctrl+z)
  • 您可以处理匹配或输入失败的情况,包括在下次尝试读取之前从输入缓冲区中删除任何有问题的字符;最后
  • 您有很好的输入(返回指示所有预期的、已发生的转换)
  • 这其中没有什么神奇之处,但它确实需要了解可能的错误条件,并在每个输入上处理它们

    在您的例子中,让我们看看您从用户那里获取要输入的字数的任务。在这里,您试图使用
    fgets
    (这很好!)进行读取,但未能提供足够的存储来保存输入。当从文本中读取少量文本时
    please enter string #0: you've entered: 
    word length: 1
    
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    
    #define BUF_SIZE_WORDS 4096
    #define BUF_SIZE_NUMBERS 256
    #define MAX_WORDS 10
    
    int word_input(int num_words);
    
    void empty_stdin();
    
    int main(void)
    {
        int num_words = 0,       /* number of words to enter */
            word_count_check = 0;          /* word count */
    
        char buffer[BUF_SIZE_NUMBERS] = "";    /* buffer of sufficient size for input */
    
        for (;;) /* loop continually until valid input of NUMBER OF WORDS USER WANTS TO ENTER or user cancels */
        {
            printf ("how many words would you like to enter? [1-%d]: ", MAX_WORDS);
            // check for cancellation of input
            if (!fgets (buffer, BUF_SIZE_NUMBERS, stdin))
            {
                fputs ("user canceled input\n", stderr);
                return 1;
            }
    
            // check if user simply hit enter w/o typing anything
            if(buffer[0] == '\n')
            {
                printf("please enter a value\n");
                continue;
            }
    
    
            size_t inlength = strlen(buffer);
    
            // validate length < BUF_SIZE_NUMBERS - 1
            if (inlength >= BUF_SIZE_NUMBERS - 1)
            {
                fputs ("input exceeds allocated buffer size\n", stderr);
                return 2;
            }
    
            if (inlength && buffer[inlength - 1] == '\n')
            {
                // printf("hurray!\n");
                buffer[--inlength] = 0;
            }
            else if (inlength == BUF_SIZE_NUMBERS - 1) /* the line was too long */
            {
                printf("you've entered too many characters... please stick to a maximum of %i\n", BUF_SIZE_NUMBERS);
                empty_stdin();
                continue;
            }
    
            // make sure user actually entered a proper int
            if (sscanf (buffer, "%d", &num_words) != 1) /* sscanf is used for conversion */
            {
                fputs ("invalid conversion to int; please provide valid input\n", stderr);
                continue;
            }
    
            // check if the number entered is out of range
            if (num_words < 1 || num_words > MAX_WORDS)
                fprintf (stderr, "%2d out of valid range.\n", num_words);
            else
                break; /*if the input has been validated, we can now break out of the for loop */
        }
    
        // call the word_input function and store its return value in word_count_check
        word_count_check = word_input(num_words);
    
        // check if the number of words processed equals to the number requested by the user
        if(word_count_check == num_words)
        {
            printf("success!\n");
        }
        else
        {
            printf("something went wrong, since word_count_check != num_words...\n");
        }
    
    }
    
    int word_input(int num_words)
    {
        int word_count = 0;
    
        for(;;) /* loop until word_count == num_words is achieved */
        {
            // declare an array for storing input string
            char buffer[BUF_SIZE_WORDS];
            char valid_input[BUF_SIZE_WORDS];
    
            // prompt user for input
            printf("please enter a string: ");
    
            // get input and check for CTRL+D
            if (!fgets(buffer, BUF_SIZE_WORDS, stdin))
            {
                fputs ("user canceled input\n", stderr);
                exit(1);
            }
    
             // check if user simply hit enter w/o typing anything
            if(buffer[0] == '\n')
            {
                printf("please enter a word that's more than 0 characters\n");
                // empty_stdin();
                continue;
            }
    
            size_t inlength = strlen(buffer);
    
            // check if user input exceed buffer size
            if (inlength >= BUF_SIZE_WORDS - 1)
            {
                empty_stdin();
                fputs ("input exceeds allocated buffer size, please try again\n", stderr);
                continue;
            }
    
            // check if the user entered too many characters
            if (inlength == BUF_SIZE_WORDS - 1) /* the line was too long */
            {
                printf("you've entered too many characters... please stick to a maximum of %i\n", BUF_SIZE_WORDS);
                empty_stdin();
                continue;
            }
    
            if (inlength && buffer[inlength - 1] == '\n')
            {
                buffer[--inlength] = 0;
    
                // get rid of trailing spaces using sscanf
                sscanf(buffer, "%s", valid_input);
    
                // figure out the length of the word the user entered
                int word_length = ((int) strlen(valid_input));
                printf("string length: %i\n", word_length);
    
                // print out the word entered by the user one character at a time
                printf("you've entered: ");
                for (int i = 0; i < word_length; i++)
                {
                    printf("%c", valid_input[i]);
                }
                printf("\n");
    
                // increment word count
                word_count++;
                printf("word_count = %i\n", word_count);
    
                if (word_count == num_words)
                {
                    return word_count;
                }
            }
        }
    }
    
    /* helper function to remove any chars left in input buffer */
    void empty_stdin()
    {
        int c = getchar();
        while (c != '\n' && c != EOF)
            c = getchar();
    }
    
    if (!fgets (buf, MAXC, stdin)) { /* validate ALL user input */ 
        fputs ("(user canceled input)\n", stderr); 
        return 1; 
    }
    
    void empty_stdin()
    {
        int c = getchar();
        while (c != '\n' && c != EOF)
            c = getchar();
    }
    
    #include <stdio.h>
    
    #define SIZE  10    /* good form defining a constant! */
    #define MAXC 256    /* max characters for buffer */
    
    int main (void) {
    
        int nwords = 0,         /* number of words to enter */
            words = 0,          /* each word */
            wc = 0;             /* word count */
        char buf[MAXC] = "";    /* buffer of sufficient size for input */
    
        for (;;) {  /* loop continually until valid input or user cancels */
            printf ("number of words to enter? [1-%d]: ", SIZE);
            if (!fgets (buf, MAXC, stdin)) {    /* validate ALL user input */
                fputs ("(user canceled input)\n", stderr);
                return 1;
            }
            /* validate length < MAXC - 1 and buf[length-1] == '\n' here */
    
            if (sscanf (buf, "%d", &nwords) != 1) { /* sscanf for conversion */
                fputs ("  error: invalid conversion to int.\n", stderr);
                continue;
            }
            if (nwords < 1 || SIZE < nwords)  /* validate nwords in range */
                fprintf (stderr, " %2d out of valid range.\n", nwords);
            else  /* good input received, break loop */
                break;
        }
    
        printf ("\nnumber of words entered: %d\n", nwords);
        for (; wc < nwords;) {  /* loop continually  */
            int rtn = 0;    /* scanf return */
            printf ("please enter a number between 1 and %d: ", SIZE);
            rtn = scanf ("%d", &words);     /* valdate ALL user input */
            if (rtn == EOF) {           /* handle EOF (manual) */
                fputs ("(user canceled input)\n", stderr);
                break;
            }
            else if (rtn == 0) {    /* handle "matching failure" */
                int c = getchar();  /* remove offending chars from stdin */
                while (c != '\n' && c != EOF)
                    c = getchar();
                fputs ("  error: invalid integer input\n", stderr);
                continue;
            }
            else {  /* valid integer received */
                int c = getchar();      /* remove any extra chars from stdin */
                while (c != '\n' && c != EOF)
                    c = getchar();
                if (words < 1 || SIZE < words)  /* validate in-range */
                    fprintf (stderr, " %2d - invalid! (1 < valid < %d)\n", 
                            words, SIZE);
                else    /* good input, increment word count */
                    printf (" word[%2d]: %3d\n", ++wc, words);
            }
        }
    
    /* helper function to remove any chars left in input buffer */
    void empty_stdin()
    {
        int c = getchar();
        while (c != '\n' && c != EOF)
            c = getchar();
    }
    
    #include <stdio.h>
    
    #define SIZE  10    /* good form defining a constant! */
    #define MAXC 256    /* max characters for buffer */
    
    int main (void) {
    
        int nwords = 0,         /* number of words to enter */
            words = 0,          /* each word */
            wc = 0;             /* word count */
        char buf[MAXC] = "";    /* buffer of sufficient size for input */
    
        for (;;) {  /* loop continually until valid input or user cancels */
            printf ("number of words to enter? [1-%d]: ", SIZE);
            if (!fgets (buf, MAXC, stdin)) {    /* validate ALL user input */
                fputs ("(user canceled input)\n", stderr);
                return 1;
            }
            /* validate length < MAXC - 1 and buf[length-1] == '\n' here */
    
            if (sscanf (buf, "%d", &nwords) != 1) { /* sscanf for conversion */
                fputs ("  error: invalid conversion to int.\n", stderr);
                continue;
            }
            if (nwords < 1 || SIZE < nwords)
                fprintf (stderr, " %2d out of valid range.\n", nwords);
            else 
                break;
        }
    
        printf ("\nnumber of words entered: %d\n", nwords);
        for (; wc < nwords;) {  /* loop continually  */
            int rtn = 0;    /* scanf return */
            printf ("please enter a number between 1 and %d: ", SIZE);
            rtn = scanf ("%d", &words);     /* valdate ALL user input */
            if (rtn == EOF) {           /* handle EOF (manual) */
                fputs ("(user canceled input)\n", stderr);
                break;
            }
            else if (rtn == 0) {    /* handle "matching failure" */
                int c = getchar();  /* remove offending chars from stdin */
                while (c != '\n' && c != EOF)
                    c = getchar();
                fputs ("  error: invalid integer input\n", stderr);
                continue;
            }
            else {  /* valid integer received */
                int c = getchar();      /* remove any extra chars from stdin */
                while (c != '\n' && c != EOF)
                    c = getchar();
                if (words < 1 || SIZE < words)  /* validate in-range */
                    fprintf (stderr, " %2d - invalid! (1 < valid < %d)\n", 
                            words, SIZE);
                else    /* good input, increment word count */
                    printf (" word[%2d]: %3d\n", ++wc, words);
            }
        }
    }
    
    $ ./bin/getintstdin
    number of words to enter? [1-10]: five, maybe six?
      error: invalid conversion to int.
    number of words to enter? [1-10]: -2
     -2 out of valid range.
    number of words to enter? [1-10]: 3
    
    number of words entered: 3
    please enter a number between 1 and 10: two? three?
      error: invalid integer input
    please enter a number between 1 and 10: 2
     word[ 1]:   2
    please enter a number between 1 and 10: -2
     -2 - invalid! (1 < valid < 10)
    please enter a number between 1 and 10: 11
     11 - invalid! (1 < valid < 10)
    please enter a number between 1 and 10: 3
     word[ 2]:   3
    please enter a number between 1 and 10: 4
     word[ 3]:   4