Требуется ли проверить возвращаемое значение fclose? Если мы успешно открыли файл, каковы шансы его закрытия?
Спасибо!
С уважением, Джей
Требуется ли проверить возвращаемое значение fclose? Если мы успешно открыли файл, каковы шансы его закрытия?
Спасибо!
С уважением, Джей
Когда вы fwrite
в файл, он может ничего не писать, он может оставаться в буфере (внутри объекта FILE). Вызов fflush
будет фактически записывать его на диск. Эта операция может выйти из строя, например, если вы просто закончили дисковое пространство или возникла некоторая другая ошибка ввода-вывода.
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() отказ не был обнаружен, программа пошел прямо вперед и уничтожил наилучшая версия данных в благосклонность поврежденной версии. И в качестве У Мерфи было бы это, жертва в этот конкретный инцидент был лицо, отвечающее за отдел, лицо с полномочиями купить больше нашего продукта или заменить это с конкурирующим продуктом - и, natch, человек, который уже недовольны нами по другим причинам.
Было бы просто приписать все последующее несчастье этому синглу упущение, но, возможно, стоит указать что и клиент, и мой бывшая компания с тех пор исчезла от корпоративной экологии.
ПРОВЕРЬТЕ КОДЫ НЕИСПРАВНОСТИ!
Вы могли (и должны) сообщить об ошибке, но в некотором смысле поток все еще закрыт:
После вызова функции fclose() любое использование потока приводит к поведению undefined.
fclose()
перед тем, как вернуться, выровнят любой неписанный вывод (через fflush()
), поэтому ошибка, полученная из базового write()
, не будет отображаться в fwrite()
или fprintf()
, но когда вы делаете fclose()
. В результате любая ошибка, которую может генерировать write()
или fflush()
, может быть сгенерирована с помощью fclose()
.
fclose()
также вызовет close()
, который может генерировать ошибки для клиентов NFS, где измененный файл фактически не загружается на удаленный сервер до close()
времени. Если NFS-сервер потерпел крах, то close()
завершится с ошибкой, и, таким образом, fclose()
также завершится с ошибкой. Это может быть справедливо для других сетевых файловых систем.
Вы должны ВСЕГДА проверить результат fclose()
Предположим, вы генерируете данные. У вас есть старые данные, которые вы fread()
из файла, а затем выполняете некоторую обработку данных, генерируете больше данных и затем записываете их в новый файл. Вы стараетесь не перезаписывать старый файл, потому что знаете, что попытка создать новый файл может завершиться неудачей, и вы хотели бы сохранить свои старые данные в этом случае (некоторые данные лучше, чем никакие данные). По завершении всех fwrite()
s, которые все успешны (потому что вы тщательно проверили возвращаемое значение из fwrite()
), вы fclose()
файл. Затем вы rename()
ваш только что записанный файл и перезапишите старый файл.
Если fclose()
не удалось из-за ошибки записи (полный диск?), вы просто перезаписали свой последний хороший файл с чем-то, что может быть нежелательным. К сожалению.
Итак, если это важно, вы должны проверить возвращаемое значение fclose()
.
В терминах кода:
#include <stdio.h>
#include <stdlib.h>
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.
Цитирование:
Системный вызов 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 может выйти из строя, заключается в том, что есть какие-либо данные, все еще буферизированные, и неявный fflush терпит неудачу. Я рекомендую всегда вызывать fflush и выполнять любую обработку ошибок.
Я видел много раз fclose(), возвращающих ненулевое значение.
И при тщательной проверке выяснилось, что фактическая проблема заключалась в записи, а не в fclose.
Поскольку записываемый материал буферизуется до того, как произойдет фактическая запись, и когда вызывается fclose(), весь буфер очищается. Таким образом, любая проблема в написании буферизованного суффикса, например, как полный диск, появляется во время fclose(). Как говорит Дэвид Йелл, для написания пуленепробиваемого приложения вам нужно рассмотреть возвращаемое значение fclose().
Если в контексте вашего приложения вы можете придумать что-нибудь полезное, если fclose() терпит неудачу, а затем проверьте возвращаемое значение. Если вы не можете, не делайте этого.
В некотором смысле закрытие файла никогда не прерывается: ошибки возвращаются, если ожидающая операция записи завершилась неудачно, но поток будет закрыт.
Чтобы избежать проблем и обеспечить (насколько это возможно из программы C), я предлагаю вам:
fwrite()
.fflush()
перед закрытием потока. Не забывайте проверять ошибки, возвращаемые fflush()
.