fclose проверка возвращаемого значения - PullRequest
27 голосов
/ 23 декабря 2009

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

Спасибо!

С уважением, Джей

Ответы [ 11 ]

36 голосов
/ 23 декабря 2009

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

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

30 голосов
/ 23 декабря 2009

С 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 () не хватило места на диске, пытаясь написать последние пару блоков данные, поэтому "временный файл" был усечен и непригодный. И так как fclose () сбой не обнаружен, программа пошел прямо вперед и уничтожил лучшая существующая версия данных в пользу испорченной версии. И в качестве Мерфи получит это, жертва в этот конкретный инцидент был ответственный за клиента отдел, человек с властью купить больше нашего продукта или заменить это с продуктом конкурента - и, конечно же, человек, который был уже недовольны нами по другим причинам.

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

ПРОВЕРЬТЕ ЭТИ КОДЫ ОТКАЗА!

5 голосов
/ 23 декабря 2009

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

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

3 голосов
/ 23 декабря 2009
  1. fclose() сбросит все неписанные выходные данные (через fflush()) перед возвратом, поэтому результаты ошибок из нижележащего write() не будут сообщаться в fwrite() или fprintf() времени, но когда Вы делаете fclose(). В результате любая ошибка, которую может генерировать write() или fflush(), может быть сгенерирована с помощью fclose().

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

2 голосов
/ 23 декабря 2009

Допустим, вы генерируете данные. У вас есть старые данные, которые вы fread() из файла, затем выполняете некоторую обработку данных, генерируете дополнительные данные и затем записываете их в новый файл. Будьте осторожны, чтобы не перезаписать старый файл, потому что вы знаете, что попытка создать новый файл может потерпеть неудачу, и вы хотели бы сохранить ваши старые данные в этом случае (некоторые данные лучше, чем никаких данных). После завершения всех fwrite() с, что все успешно (потому что вы тщательно проверили возвращаемое значение из 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).

2 голосов
/ 23 декабря 2009

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

1 голос
/ 23 декабря 2009

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

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

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

1 голос
/ 23 декабря 2009

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

1 голос
/ 23 декабря 2009

Справочная страница 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 () завершится неудачей, в основном, если вы не можете на самом деле записать / сохранить файл.

0 голосов
/ 25 декабря 2009

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

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

  1. Правильно обрабатывать ошибки, возвращаемые fwrite().
  2. Позвоните fflush() перед закрытием потока. Do не забудьте проверить наличие ошибок, возвращаемых fflush().
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...