使用fgets函数时遇到EOF

12

我正在编写一个执行某些身份验证操作的函数。我有一个文件,其中包含所有以以下结构构建的用户ID:密码:标志对:

Users.txt

user_123:a1b2:0 user_124:a2b1:1 user_125:a2b2:2

这是代码:

int main(){
    /*...*/

    /*user_id, password retrieving*/
    USRPSW* p = malloc(sizeof(USRPSW));
    if(p == NULL){
        fprintf(stderr, "Dynamic alloc error\n");
        exit(EXIT_FAILURE);
    }
    memset((void*)p, 0, sizeof(USRPSW));

    if(usr_psw_read(acc_sock_ds, p->user_id, USR_SIZE) <= 0){
        printf("Failed read: connection with %s aborted.\n",
                 inet_ntoa(client_addr.sin_addr));
        close(acc_sock_ds);
        continue;
    }

    if(usr_psw_read(acc_sock_ds, p->password, PSW_SIZE) <= 0){
        printf("Failed read: connection with %s aborted.\n",
                 inet_ntoa(client_addr.sin_addr));
        close(acc_sock_ds);
        continue;
    }

    /*Authentication through user_id, password*/
    FILE *fd;
    fd = fopen(USERSFILE, "r");
    if(fd == NULL){
        fprintf(stderr, "Users file opening error\n");
        exit(EXIT_FAILURE);
    }

    char *usr_psw_line = malloc(USR_SIZE+PSW_SIZE+3+1);
    if(usr_psw_line == NULL){
        fprintf(stderr, "Dynamic alloc error\n");
        exit(EXIT_FAILURE);
    }

    while(1){

        memset((void*)usr_psw_line, 0, sizeof(USR_SIZE+PSW_SIZE+3+1));
        fgets(usr_psw_line, USR_SIZE+PSW_SIZE+3+1, fd);
        printf("%s\n", usr_psw_line);
        fseek(fd, 1, SEEK_CUR);


        /*EOF management*/
        /*usr_id - password matching checking */

    }   
/*...*/    
}

我该如何处理EOF到达的情况?我发现当遇到EOF时,fgets不会再编辑usr_psw_line了,但也不会返回NULL指针。如果遇到EOF,这意味着在用户文件中没有找到匹配项,循环就会终止。

有人能给我一些提示或建议吗?


如果在没有读取任何字符的情况下到达文件结尾,fgets必须返回NULL。无论如何,在fgets未读取任何内容后,您可以检查feof(fd) - Daniel Fischer
我担心EOF没有被设置。我只写了一个带有记录的文件。我还应该明确设置EOF吗?你怎么做到这一点? - Fabio Carello
在读取文件时,不是到达文件末尾了吗?如果已经到达文件末尾,您不需要设置EOF,下一次尝试从中读取数据时,*fd中的标志将被设置,因此如果一切正常,feof(fd)将返回true。如果一切都不像应该那样工作,那就有点麻烦了。 - Daniel Fischer
1
EOF是一种状态,而不是流的一部分。想象一下,流就像来自水龙头的水。当你打开水龙头直到它流尽时,你会得到更多的水(另一种颜色?)来表示没有更多的水了吗? - pmg
2个回答

29

fgets()在遇到文件末尾或错误情况时返回空指针。

(EOF是一个宏,指定了某些其他函数在类似情况下返回的值;它不仅仅是短语“文件结束”的缩写。)

您正在忽略 fgets() 返回的结果。不要这样做。

请注意,仅检查 feof(fd) 不会做您想要的事情。feof() 在达到文件结尾时返回true。如果遇到错误,则 feof() 仍然返回false,如果您使用 feof() 来判断何时完成,则会陷入无限循环。并且直到读取输入失败后才返回true。

大多数C输入函数返回一些特殊值来指示没有更多内容可读。fgets()的特殊值为NULLfgetc()的特殊值为EOF等。如果愿意,您可以在此之后调用 feof() 和/或 ferror() 来确定为什么没有更多内容可读。


1
(微笑着说)我不是在之前的回答中已经说过了吗? - Steve Valliere
4
大致上是这样,但我尝试更明确地阐述潜在的概念。 - Keith Thompson

3
您可以在循环中尝试类似以下的代码:
while(1)
{
    memset((void*)usr_psw_line, 0, sizeof(USR_SIZE+PSW_SIZE+3+1));
    if( !fgets(usr_psw_line, USR_SIZE+PSW_SIZE+3+1, fd)
     || ferror( fd ) || feof( fd ) )
    {
        break;
    }
    printf("%s\n", usr_psw_line);
    fseek(fd, 1, SEEK_CUR);

    /*EOF management*/
    /*usr_id - password matching checking */

}

在增加了额外的代码后,循环将会在以下几种情况下终止:如果 fgets 返回 NULL(即没有更多数据可读取),或者已经读取到了EOF标记,或者在文件上发生了任何错误。我知道这可能是多余的,但这些测试对我总是有效的。


2
在我的看法中,最好使用 while (fgets(usr_psw_line, sizeof(usr_psw_line), fd) != 0) 作为循环条件。实际上,在读取之前没有必要将内存清零。而且,在那里测试 ferror()feof() 没有任何好处(因为它们只有在从 fgets() 返回 NULL 时才会评估为真;因此,它们永远不会导致循环退出)。 - Jonathan Leffler
同意。我尽量保留原始代码,以便直接回答问题的部分更清晰。但是在我的所有文件读取循环中,我确实做到了你所描述的。 - Steve Valliere
2
@chux:我的观点是,如果在调用fgets()时设置了错误标志,那么fgets()必须失败。然而,我实际上找不到标准中强制要求这样做的措辞,尽管我会惊讶地发现没有一个实现会像这样行事。如果跟踪流的状态,在失败后除非使用clearerr(),否则将不再使用它。还有一种不太可能的边缘情况可用。测试feof()是无意义的。我坚持我的日常使用观察结果。如果我担心流的错误状态,我会在进入函数时使用ferror() - Jonathan Leffler
1
在这个例子中,代码“sizeof(USR_SIZE+PSW_SIZE+3+1)”总是计算整数的大小 - 在许多现代机器上,它始终为4,而不管USR_SIZE和PSW_SIZE的值如何 - “sizeof(42)”(或任何其他较小的数字)将返回相同的值。我猜测封闭的“sizeof(...)”是一个错误。无论如何,清零整个缓冲区都是过度的 - 如果对fgets()的行为持怀疑态度 - 一种基于C字符串的函数 - 那么“usr_psw_line[0] = '\0';”就足够了。 - CarlRJ
1
如果要使用feof()和/或ferror(),则应该在调用fgets()之前调用它们 - 以确定给定文件是否已经处于EOF或错误状态,或在fgets()返回NULL后调用它们,以确定为什么fgets()返回NULL。在当前形式下,它们除了误导查看代码的人之外几乎没有其他作用。 - CarlRJ
显示剩余2条评论

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