C 有人能解释为什么当我输入的字符串大小超过允许值时,我的FGET会跳过行吗

C 有人能解释为什么当我输入的字符串大小超过允许值时,我的FGET会跳过行吗,c,fgets,getchar,C,Fgets,Getchar,我在使用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: ");

我在使用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的FGET将被跳过,否则它可以正常工作。我想知道我的fgets在这种情况下是如何工作的?最大长度为10。我尝试用while(getchar()!='\n')清除缓冲区;它可以工作,但我必须按enter键两次

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

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

int ch;

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

基本上从缓冲区读取并丢弃值,直到达到
\n
EOF
为止。

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

如果目标缓冲区中不存在换行符,则有3个选项:

  • 输入的行比目标可以容纳的行长。在这种情况下,您必须一直调用
    fgets
    ,直到缓冲区中有换行符
  • 您可以使用此功能“清理”输入缓冲区:

    void clean_stdin(void)
    {
        int c;
        while((c = getchar()) != '\n' && c != EOF);
    }
    
    然后,当你意识到这个世界上没有新行时,你可以调用它 目标缓冲区。但不要每次调用
    fgets
    时都调用它,您只能 当您知道输入中还有字符时,调用此函数 缓冲区(当目标缓冲区中没有换行符时), 否则,函数将等待用户输入

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

    如果
    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
    ,但它保证您可以阅读整个文档 行,该函数负责为目标分配足够的内存


  • 它不会被跳过。它正在吞噬上一次调用尚未完成的内容。如果输入的行长度超过目标缓冲区所能容纳的长度,则
    fgets
    将读取它所能读取的所有内容,直到目标已满。其余的输入仍保留在输入缓冲区中,因此下一个
    fgets
    调用将读取剩下的内容。您是否建议清除缓冲区?fflush(stdin)似乎对我不起作用,fseek(stdin,0,SEEK_END)
    fflush(stdin)
    是未定义的行为。您可以
    getchar
    ,直到遇到
    \n
    为止
    stdin
    不是可搜索流,您不能将
    fseek
    stdin
    void clean_stdin(void)
    {
        int c;
        while((c = getchar()) != '\n' && c != EOF);
    }
    
    char *line = NULL;
    size_t n = 0;
    
    if(getline(&line, &n, stdin) == -1)
    {
        // error
    }
    
    ...
    free(line);
    
    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;
    }