fclose返回值检查

是否需要检查fclose的返回值? 如果我们已成功打开文件,它可能无法关闭的可能性有多大?

谢谢!

问候,杰伊

当你fwrite到一个文件,它可能实际上没有写任何东西,它可能留在缓冲区(在FILE对象内)。 调用fflush实际上会将其写入磁盘。 该操作可能会失败 ,例如,如果您的磁盘空间不足,或者存在其他一些I / O错误。

fclose隐式刷新缓冲区,因此可能由于同样的原因而失败。

来自comp.lang.c :

fclose()调用可能会失败,并且应该像所有其他文件操作一样进行错误检查。 听起来很迂腐吧? 错误。 在以前的生活中,我公司的产品通过在关闭文件时省略检查失败来设法销毁客户的数据。 顺序就像(释义):

 stream = fopen(tempfile, "w"); if (stream == NULL) ... while (more_to_write) if (fwrite(buffer, 1, buflen, stream) != buflen) ... fclose (stream); /* The new version has been written successfully. Delete * the old one and rename. */ remove (realfile); rename (tempfile, realfile); 

当然,发生的事情是fclose()耗尽磁盘空间试图写入最后几个数据块,因此`tempfile’被截断并且无法使用。 由于未检测到fclose()故障,程序正在进行并销毁现有最佳版本的数据,以支持受损版本。 而且,正如墨菲所说的那样,这次特殊事件的受害者是客户部门的负责人,有权购买更多产品或用竞争对手的产品代替的人 – 而且,他是一个人。由于其他原因,我们已经对我们不满意了。

将所有随之而来的痛苦归咎于这一遗漏是一件容易的事,但值得指出的是,顾客和我以前的公司都已经从企业生态学中消失了。

检查这些失败的代码!

您可以(并且应该)报告错误,但从某种意义上说, 流仍然是关闭的 :

在调用fclose()之后,对流的任何使用都会导致未定义的行为。

  1. fclose()将在返回之前刷新任何未write()输出(通过fflush() ),因此底层write()的错误结果将不会在fwrite()fprintf()时报告,但是当你执行fclose() 。 因此,可以通过fclose()生成write()fflush()可以生成的任何错误。

  2. fclose()也会调用close() ,这会在NFS客户端上产生错误,其中更改的文件实际上不会上传到远程服务器,直到close()时间。 如果NFS服务器崩溃,则close()将失败,因此fclose()也将失败。 对于其他网络文件系统可能也是如此。

你必须总是检查fclose()的结果

假设您正在生成数据。 您有来自文件的fread()旧数据,然后对数据进行一些处理,生成更多数据,然后将其写入新文件。 您要小心不要覆盖旧文件,因为您知道尝试创建新文件可能会失败,并且您希望在这种情况下保留旧数据(某些数据优于无数据)。 在完成所有成功的fwrite()之后(因为你仔细检查了fwrite()的返回值),你fclose()文件。 然后, rename()刚刚写入的文件并覆盖旧文件。

如果fclose()由于写入错误(磁盘已满? fclose()失败,那么您只是用可能是垃圾的东西覆盖了您的上一个好文件。 哎呀。

因此,如果它很重要,您应该检查fclose()的返回值。

在代码方面:

 #include  #include  int main(void) { FILE *ifp = fopen("in.dat", "rb"); FILE *ofp = fopen("out.dat", "wb"); char buf[BUFSIZ]; size_t n; int success = 1; if (ifp == NULL) { fprintf(stderr, "error opening in.dat\n"); perror("in.dat"); return EXIT_FAILURE; } if (ofp == NULL) { fclose(ifp); fprintf(stderr, "error opening out.dat\n"); perror("out.dat"); return EXIT_FAILURE; } while ((n = fread(buf, 1, sizeof buf, ifp)) > 0) { size_t nw; if ((nw = fwrite(buf, 1, n, ofp)) != n) { fprintf(stderr, "error writing, wrote %lu bytes instead of %lu\n", (unsigned long)n, (unsigned long)nw); fclose(ifp); fclose(ofp); return EXIT_FAILURE; } } if (ferror(ifp)) { fprintf(stderr, "ferror on ifp\n"); fclose(ofp); fclose(ifp); return EXIT_FAILURE; } #ifdef MAYLOSE_DATA fclose(ofp); fclose(ifp); rename("out.dat", "in.dat"); /* Oops, may lose data */ #else if (fclose(ofp) == EOF) { perror("out.dat"); success = 0; } if (fclose(ifp) == EOF) { perror("in.dat"); success = 0; } if (success) { rename("out.dat", "in.dat"); /* Good */ } #endif return EXIT_SUCCESS; } 

在上面的代码中,我们一直小心fopen()fwrite()fread() ,但即使这样,不检查fclose()可能会导致数据丢失(当使用MAYLOSE_DATA定义编译时)。

fclose失败的一个原因是,如果仍有任何数据仍然被缓冲并且隐式fflush失败。 我建议总是调用fflush并在那里进行任何error handling。

我已经多次看到fclose()返回非零值。

经过仔细检查发现,实际问题是写的而不是fclose。

由于正在写入的内容在实际写入发生之前被缓冲,并且当调用fclose()时,所有缓冲区都被刷新。 因此,在fclose()期间出现写缓冲后缀的任何问题,比如磁盘已满。 正如David Yell所说,要编写防弹应用程序,您需要考虑fclose()的返回值。

fclose手册页引用它可能因close或fflush可能失败的任何原因而失败。

引用:

如果出现以下情况,close()系统调用将失败:

  [EBADF] fildes is not a valid, active file descriptor. [EINTR] Its execution was interrupted by a signal. [EIO] A previously-uncommitted write(2) encountered an input/output error. 

fflush可能会因write ()失败而失败,主要是因为您无法实际写入/保存文件。

如果在应用程序的上下文中,如果fclose()失败,您可以考虑做一些有用的事情,然后测试返回值。 如果你不能,不要。

从某种意义上说, 关闭文件永远不会失败 :如果挂起的写入操作失败,则会返回错误,但关闭该流。

为避免出现问题并确保(尽可能从C程序中),我建议您:

  1. 正确处理fwrite()返回的错误。
  2. 在关闭流之前调用fflush() 。 请记得检查fflush()返回的错误。