проверка возвращаемого значения fclose

Требуется, чтобы это проверяет возвращаемое значение fclose? Если мы успешно открыли файл, каковы возможности, что он может не закрыться?

Спасибо!

С уважением, сойка

27
задан Mark Elliot 23 December 2009 в 17:54
поделиться

11 ответов

Когда вы записываете в файл, он может на самом деле ничего не записать, он может остаться в буфере (внутри объекта FILE). Вызов fflush на самом деле запишет его на диск. Эта операция может не сработать , например, если вы только что закончили дисковое пространство, или есть какая-то другая ошибка ввода/вывода.

fclose тоже неявно промывает буферы, так что по тем же причинам она может не сработать.

.
36
ответ дан 28 November 2019 в 04:19
поделиться

Одна из причин, по которой fclose может дать сбой, заключается в том, что любые данные все еще буферизируются, а неявный сбой fflush. Я рекомендую всегда вызывать fflush и обрабатывать там любые ошибки.

.
1
ответ дан 28 November 2019 в 04:19
поделиться

Я много раз видел, как fclose() возвращает ненулевое значение.

И при тщательном изучении выяснилось, что на самом деле проблема была с записью, а не с fclose.

Так как записываемый материал буферизируется до реальной записи и при вызове функции fclose() весь буфер очищается. Так что любая проблема при записи буферизованного суффикса, скажем, как при заполненном диске, возникает во время fclose(). Как говорит Дэвид Йелл (David Yell), для записи пуленепробиваемого приложения необходимо учитывать возвращаемое значение функции fclose().

.
1
ответ дан 28 November 2019 в 04:19
поделиться

Вы должны ВСЕГДА проверять результат fclose()

.
2
ответ дан 28 November 2019 в 04:19
поделиться

Допустим, вы генерируете данные. У вас есть старые данные, которые вы fread() из файла, а затем выполняете некоторую обработку данных, генерируете больше данных, а затем записываете их в новый файл. Вы осторожно не перезаписываете старый файл, потому что знаете, что попытка создать новый файл может не увенчаться успехом, и вы хотели бы сохранить ваши старые данные в этом случае (некоторые данные лучше, чем нет данных). После завершения всех fwrite()s, которые все успешно завершены (так как вы тщательно проверили возвращаемое значение из fwrite()), вы fclose() файла. Затем вы переименовываете() только что написанный файл и перезаписываете старый файл.

Если 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).

2
ответ дан 28 November 2019 в 04:19
поделиться

Из comp.lang.c:

вызов fclose() может не сработать, и должен так же усердно проверяться на ошибки как и все другие файловые операции. Звучит педантично, да? Неправильно. В прошлая жизнь, продукт моей компании удалось уничтожить данные клиента пропустив проверку на неудачу, когда закрытие файла. Последовательность пошла что-то вроде (перефразируя):

stream = fopen(tempfile, "w"); 
if (stream == NULL) ... 
 while (more_to_write) 
 если (fwrite(buffer, 1, buflen, stream) != buflen) ... 
fclose (поток); 

/* Новая версия написана успешно. Удалить 
 * старое и переименованное. 
 */ 
удалить (реальный файл); 
переименование (tempfile, realfile); 

Конечно, случилось то, что fclose() исчерпала дисковое пространство при попытке написать последние пару блоков данные, так что ``температурный файл'' был усечен и непригоден. А так как fclose() сбой не был обнаружен, программа пошёл вперёд и уничтожил лучшая сохранившаяся версия данных в услуга за поврежденную версию. И, как Мерфи получил бы его, жертва в именно этот инцидент и был ответственное лицо департамент, лицо с полномочиями купить больше нашей продукции или заменить с продуктом конкурента... и, Нэтч, человек, который уже был недоволен нами по другим причинам.

Было бы неплохо приписать все вытекающее из этого несчастье упущение, но, возможно, стоит отметить что и клиент, и мой прежняя компания с тех пор исчезла из корпоративной экологии.

КОДЫ БЕЗОПАСНОСТИ!

30
ответ дан 28 November 2019 в 04:19
поделиться

В man-странице fclose указано, что она может не сработать по любой из причин, по которой может не сработать функция close или fflush.

Цитирую:

Системный вызов функции close() будет неудачным, если:

.
  [EBADF] fildes не является действительным, активным дескриптором файла.

 Его выполнение было прервано сигналом.

 [EIO] Ранее нефиксированная запись(2) встретила
 ошибка ввода/вывода.

fflush может не сработать по причинам, по которым write() не сработает, в основном, в том случае, если вы не сможете на самом деле записать/сохранить файл.

.
1
ответ дан 28 November 2019 в 04:19
поделиться
  1. fclose() промоет любой неписанный вывод (через fflush()) перед возвращением, таким образом, ошибка, возникающая в результате работы базовой функции write(), не будет сообщена во время fwrite() или fprintf(), а при выполнении fclose(). В результате, любая ошибка, которую может сгенерировать write() или fflush(), может быть сгенерирована функцией fclose(). Также

  2. fclose() будет вызывать close(), который может генерировать ошибки на клиентах NFS, где изменённый файл на самом деле не будет загружен на удалённый сервер до времени close(). Если сервер NFS выйдет из строя, то произойдет сбой в функции close(), и таким образом fclose() также выйдет из строя. Это может быть справедливо и для других сетевых файловых систем.

3
ответ дан 28 November 2019 в 04:19
поделиться

Можно (и нужно) сообщить об ошибке, но в каком-то смысле поток все равно закрыт:

После вызова fclose() любое использование потока приводит к неопределенному поведению.

5
ответ дан 28 November 2019 в 04:19
поделиться

Если в контексте вашего приложения вы можете придумать что-нибудь полезное, если fclose() не удается, то проверьте возвращаемое значение. Если нет, то не надо.

0
ответ дан 28 November 2019 в 04:19
поделиться

В каком-то смысле закрытие файла никогда не дает сбой: ошибки возвращаются, если отложенная операция записи не удалась, а поток будет закрыт.

Чтобы избежать проблем и убедиться (насколько это возможно из программы на Си), я предлагаю:

  1. правильно обрабатывать ошибки, возвращаемые функцией fwrite().
  2. Вызов fflush() перед закрытием потока. Do remember to check for errors return by fflush().
0
ответ дан 28 November 2019 в 04:19
поделиться
Другие вопросы по тегам:

Похожие вопросы: