简体   繁体   English

在搜索文件时使用 feof 有多安全?

[英]How safe is using !feof in searching a file?

I read here that feof or more precisely using !feof in searching for a info in a file is a bad habit.我在这里读到feof或更准确地说使用!feof在文件中搜索信息是一个坏习惯。

What I understood is that it's bad because it reads information from the FILE pointer before called function or process or something like that.我的理解是它很糟糕,因为它在调用 function 或进程或类似的东西之前从FILE指针读取信息。

Wouldn't it be fine to have a do / while loop with fscanf inside and !feof as the exit condition?在内部使用fscanf并以!feof作为退出条件的do / while循环不是很好吗?

This is a search function that I did:这是我所做的搜索 function:

void searchemployee(char *filename, char *str)
{
    employee e;
    FILE *f;
    int c;
    f = fopen(filename, "r");
    if (f == NULL)
        printf("file couldn't be loaded\n");
    else {
        c = 0;
        do {
            fscanf(f, "%s %s %d\n", e.fname, e.lname, &e.nchildren);
            if (strcmp(e.fname, str) == 0)
                c = 1;
        } while (c == 0 && !feof(f));
        if (c != 1)
            printf("employee not found\n");
        else
            printf("employee : %s %s| children : %d\n", e.fname, e.lname, e.nchildren);
    }
    fclose(f);
}

The return value of the function feof specifies whether a previous input operation has already encountered the end of the file. function feof的返回值指定之前的输入操作是否已经遇到文件末尾。 This function does not specify whether the next input will encounter the end of the file.这个function没有指定下一次输入是否遇到文件末尾。

The problem with的问题

do{
    fscanf(f,"%s %s %d\n",e.fname,e.lname,&e.nchildren);
    if (strcmp(e.fname,str)==0)
        c=1;
}while(c==0 && !feof(f));

is that if fscanf fails and returns EOF due to encountering the end of the file, then it will write nothing to e.fname .是如果fscanf失败并由于遇到文件末尾而返回EOF ,那么它不会向e.fname写入任何内容。

If this happens in the first iteration of the loop, then the content of e.fname will be indeterminate and the subsequent function call strcmp(e.fname,str) will invoke undefined behavior (ie your program may crash), unless e.fname happens to contain a terminating null character.如果这发生在循环的第一次迭代中,则e.fname的内容将不确定,随后的 function 调用strcmp(e.fname,str)将调用未定义的行为(即您的程序可能会崩溃),除非e.fname恰好包含终止字符 null。

If this does not happen in the first iteration, but rather in a subsequent iteration of the loop, then the content of e.fname will contain the content of the previous loop iteration, so you will effectively be processing the last successful call of fscanf twice.如果这在第一次迭代中没有发生,而是在循环的后续迭代中发生,则e.fname的内容将包含前一次循环迭代的内容,因此您将有效地处理fscanf的最后一次成功调用两次.

In this specific case, processing the last successful call of fscanf twice is harmless, except for being a slight waste of CPU and memory resources.在这种特定情况下,处理最后一次成功调用fscanf两次是无害的,只是会稍微浪费 CPU 和 memory 资源。 However, in most situations, processing the last input twice will result in your program not working as intended.然而,在大多数情况下,处理最后一次输入两次将导致您的程序无法按预期运行。

See the following question for further information:有关详细信息,请参阅以下问题:

Why is “while(?feof(file) )” always wrong?为什么“while(?feof(file) )”总是错误的?

If you change the loop to如果将循环更改为

for (;;) {
    fscanf(f,"%s %s %d\n",e.fname,e.lname,&e.nchildren);
    if ( c != 0 || feof(f) )
        break;
    if (strcmp(e.fname,str)==0)
        c=1;
}

so that the loop condition is checked in the middle of the loop, then the problem mentioned above will be gone.这样在循环中间检查循环条件,那么上面说的问题就没有了。

However, it is generally better to check the return value of fscanf instead of calling feof , for example like this:但是,通常最好检查fscanf的返回值而不是调用feof ,例如:

c = 0;

while ( c == 0 && fscanf(f,"%s %s %d\n",e.fname,e.lname,&e.nchildren) == 3 ) {
    if (strcmp(e.fname,str)==0)
        c=1;
}

Also, you don't need the flag variable c .此外,您不需要标志变量c I suggest that you incorporate the lines我建议你合并这些行

if (c!=1)
    printf("emplyee not found\n");
else
    printf("employee : %s %s| children : %d\n",e.fname,e.lname,e.nchildren);

partially into the loop, like this:部分进入循环,像这样:

void searchemployee( char *filename, char *str )
{
    employee e;
    FILE *f = NULL;

    //attempt to open file
    f = fopen( filename, "r" );
    if ( f == NULL )
    {
        printf( "file couldn't be loaded\n" );
        goto cleanup;
    }

    //process one employee record per loop iteration
    while ( fscanf( f, "%s %s %d\n", e.fname, e.lname, &e.nchildren ) == 3 )
    {
        //check whether we found the target record
        if ( strcmp(e.fname,str) == 0 )
        {
            printf(
                "employee : %s %s| children : %d\n",
                e.fname, e.lname, e.nchildren
            );
            goto cleanup;
        }
    }

    printf( "employee not found.\n");

cleanup:
    if ( f != NULL )
        fclose(f);
}

Another issue is that when using %s with scanf or fscanf , you should generally also add a width limit, to prevent a possible buffer overflow .另一个问题是,将%sscanffscanf一起使用时,通常还应添加宽度限制,以防止可能的缓冲区溢出 For example, if e.fname has a size of 100 characters, you should use %99s to limit the number of bytes written to 99 plus the terminating null character.例如,如果e.fname的大小为100字符,则应使用%99s将写入的字节数限制为99加上终止字符 null。

Calling feof asks the question “Was end-of-file or an error encountered in a previous operation on this stream?”调用feof会询问“是文件结尾还是在此 stream 上的先前操作中遇到错误?”

If you use feof to answer that question, that is fine.如果您使用feof来回答该问题,那很好。 But, you use feof to expect that your next operation will read data from the file, that is wrong.但是,您使用feof期望您的下一个操作将从文件中读取数据,这是错误的。 The previous operation might have ended just before the end of the file, so feof says “no,” but there is nothing left in the file to read.之前的操作可能刚好在文件末尾之前结束,所以feof说“不”,但文件中没有任何内容可供读取。

The file/stream functions in the standard C library are designed to tell you when they failed because end-of-file was reached.标准 C 库中的文件/流函数旨在告诉您它们何时因到达文件末尾而失败。 You should use the return value (or other indication) provided by each function to test for a problem:您应该使用每个 function 提供的返回值(或其他指示)来测试问题:

if (3 != fscanf(f, "%s %s %d\n", e.fname, e.lname, &e.nchildren))
{
    // Handle fact that fscanf did not read and convert 3 values.
}

int x = getchar();
if (x == EOF)
{
    // Handle fact that fscanf did not read and convert 3 values.
}

Note that calling fscanf and then feof will tell if fscanf encountered end-of-file or an input error, but it will not tell you whether fscanf read some input and assigned some values but then encountered end-of-file and did not finish.请注意,先调用fscanf再调用feof会告诉您fscanf是否遇到文件末尾或输入错误,但它不会告诉您fscanf是否读取了一些输入并分配了一些值但随后遇到了文件末尾并且没有完成。 If you are reading only one thing, you might get away with fscanf followed by feof , but a more sophisticated program may need to distinguish partial inputs.如果您只阅读一件事,您可以使用fscanf后接feof ,但更复杂的程序可能需要区分部分输入。

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

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