【问题标题】:Can someone explain why my fgets is skipping lines when I enter more then allowed string size有人可以解释为什么当我输入超过允许的字符串大小时我的 fgets 会跳行吗
【发布时间】:2018-04-16 19:59:37
【问题描述】:

我在使用 stdin 缓冲区时遇到问题,如果有任何见解,我将不胜感激,我有这个函数可以接受用户输入的姓氏

void lastName(int *counter, User *pt) {

    for (int i = *counter; i < (*counter + 1); i++) {
        pt[i].lastName = calloc (MAX_LENGTH, sizeof(char));
        printf("Enter Last Name: ");
        fgets(pt[i].lastName, MAX_LENGTH, stdin);
        strtok(pt[i].lastName, "\n");
    }
}

我也有这个函数,接受用户输入的 ID

void id(int *counter, User *pt) {

    char num[MAX_LENGTH];
    long temp;
    for (int i = *counter; i < *counter + 1; i++) {
        printf("Enter the ID of %s: ", pt[i].firstName);
        fgets(num, MAX_LENGTH, stdin);
        strtok(num, "\n");
        temp = strtol(num, NULL, 10);
        pt[i].id = (int) temp;
    }
}

这就是我在 main 中调用它们的方式

lastName(&counter, pt);
id(&counter, pt);

如果我输入一个很长的姓氏,它会被剪切并显示为 MAX_LENGTH,但 ID 的 fgets 会被跳过,否则它可以正常工作。我想知道我的 fgets 在这种情况下是如何工作的? MAX_LENGTH 为 10。我尝试使用 while(getchar() != '\n'); 清除缓冲区它可以工作,但我必须按两次回车。

Enter First Name: Test
Enter Last Name: Williamsamsmases
Enter the ID of Test: Would you like to enter a User?(Y/N):N

【问题讨论】:

  • 它没有被跳过。它正在吃掉上一个调用尚未完成的任何东西。
  • 如果您输入的行长于目标缓冲区可以容纳的长度,那么fgets 将读取它所能读取的所有内容,直到目标缓冲区已满。输入的其余部分保留在输入缓冲区中,因此下一个 fgets 调用会读取留下的内容。
  • 你推荐一些东西来清除缓冲区吗? fflush(stdin) 似乎对我不起作用, fseek(stdin, 0, SEEK_END)
  • fflush(stdin) 是未定义的行为。你可以getchar直到遇到\nstackoverflow.com/questions/7898215/…
  • stdin 不是可搜索的流,您不能将fseekstdin 一起使用

标签: c fgets getchar


【解决方案1】:

如果您输入的行长于目标缓冲区可以容纳的长度,则fgets 将读取所有内容,直到目标缓冲区已满。输入的其余部分保留在输入缓冲区中,因此下一个 fgets 调用会读取留下的内容。

如果目标缓冲区中没有换行符,您有 3 个选项:

  1. 输入的行长于目的地可以容纳的长度。在这种情况下,您必须继续调用 fgets,直到缓冲区中出现换行符。
  2. 你可以用这个函数“清理”输入缓冲区:

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

    然后你可以在你意识到没有换行符时调用它 目标缓冲区。但是不要每次调用fgets都调用,只能 当您知道输入中有字符时调用此函数 缓冲区(当目标缓冲区中没有换行符时), 否则函数将等待用户输入。

  3. 如果您的系统支持getline,那么您可以使用它来阅读 整行和getline 将负责分配所需的内存 它:

    char *line = NULL;
    size_t n = 0;
    
    if(getline(&line, &n, stdin) == -1)
    {
        // error
    }
    
    ...
    free(line);
    

    如果您的系统上没有getline,那么您可以编写一个函数 模仿getline,像这样:

    char *fgets_long(FILE *fp)
    {
        size_t size = 0, currlen = 0;
        char line[1024];
        char *ret = NULL, *tmp;
    
        while(fgets(line, sizeof line, fp))
        {
            int wholeline = 0;
            size_t len = strlen(line);
    
            if(line[len - 1] == '\n')
            {
                line[len-- - 1] = 0;
                wholeline = 1;
            }
    
            if(currlen + len >= size)
            {
                // we need more space in the buffer
                size += (sizeof line) - (size ? 1 : 0);
                tmp = realloc(ret, size);
                if(tmp == NULL)
                    break; // return all we've got so far
                ret = tmp;
            }
    
            memcpy(ret + currlen, line, len + 1);
            currlen += len;
    
            if(wholeline)
                break;
        }
    
        if(ret)
        {
            tmp = realloc(ret, currlen + 1);
            if(tmp)
                ret = tmp;
        }
    
        return ret;
    }
    

    这个函数的工作方式类似于fgets,但它保证你阅读了整个 行,该函数负责为目标分配足够的内存。

【讨论】:

    【解决方案2】:

    当您调用fgets 时,它会从stdin 读取输入,直到插入的变量已满。一旦它是,输入缓冲区上的其余输入将保留在那里。一种解决方案是在读取每个值后清除输入缓冲区。

    int ch;
    
    while ((ch = getchar()) != '\n' && ch != EOF);
    

    您实际上是从缓冲区读取并丢弃这些值,直到达到 \nEOF

    【讨论】:

      猜你喜欢
      • 2010-11-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-06
      • 1970-01-01
      • 2022-01-12
      • 2015-08-31
      相关资源
      最近更新 更多