Überprüfung des Rückgabewerts von fclose

Lesezeit: 5 Minuten

Benutzer-Avatar
Jay

Muss der Rückgabewert von fclose überprüft werden? Wenn wir eine Datei erfolgreich geöffnet haben, wie hoch ist die Wahrscheinlichkeit, dass sie nicht geschlossen werden kann?

Vielen Dank!

Grüße, Jay

  • Danke an alle für die ganzen Antworten! 🙂

    – Jay

    23. Dezember 2009 um 18:10 Uhr

  • siehe auch: stackoverflow.com/questions/569573/…

    Benutzer195488

    23. Dezember 2009 um 18:15 Uhr

  • Sie sollten erwägen, eine als richtig zu markieren.

    – Café

    23. Dezember 2009 um 21:40 Uhr

Wenn du fwrite in eine Datei schreiben, kann es sein, dass es nichts schreibt, es kann in einem Puffer bleiben (innerhalb des FILE-Objekts). Berufung fflush würde es tatsächlich auf die Festplatte schreiben. Dieser Vorgang kann fehlschlagenwenn Ihnen beispielsweise gerade der Speicherplatz ausgegangen ist oder ein anderer E/A-Fehler vorliegt.

fclose löscht die Puffer auch implizit, so dass es aus den gleichen Gründen fehlschlagen kann.

Aus comp.lang.c:

Der Aufruf von fclose() kann fehlschlagen und sollte genauso sorgfältig auf Fehler überprüft werden wie alle anderen Dateioperationen. Klingt pedantisch, oder? Falsch. In einem früheren Leben gelang es dem Produkt meiner Firma, die Daten eines Kunden zu zerstören, indem es beim Schließen einer Datei eine Prüfung auf Fehler ausließ. Die Sequenz ging in etwa so (paraphrasiert):

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); 

Was natürlich passierte, war, dass fclose() beim Versuch, die letzten paar Datenblöcke zu schreiben, der Speicherplatz ausging, sodass die `tempfile’ abgeschnitten und unbrauchbar war. Und da der fclose()-Fehler nicht erkannt wurde, machte das Programm sofort weiter und zerstörte die beste vorhandene Version der Daten zugunsten der beschädigten Version. Und, wie Murphy es ausdrücken würde, das Opfer in diesem speziellen Vorfall war die verantwortliche Person der Kundenabteilung, die Person, die befugt war, mehr von unserem Produkt zu kaufen oder es durch ein Konkurrenzprodukt zu ersetzen – und natürlich eine Person, die war schon aus anderen Gründen unzufrieden mit uns.

Es wäre zu weit hergeholt, die ganze daraus resultierende Misere diesem einzigen Versäumnis zuzuschreiben, aber es sollte darauf hingewiesen werden, dass sowohl der Kunde als auch mein ehemaliges Unternehmen inzwischen aus der Unternehmensökologie verschwunden sind.

ÜBERPRÜFEN SIE DIESE FEHLERCODES!

  • Haha. Ich dachte auch an diesen Beitrag, als ich die Frage las. Ich beschloss jedoch, nicht danach zu suchen, und schrieb darüber aus dem Gedächtnis.

    – Alok Singhal

    23. Dezember 2009 um 18:09 Uhr

  • @Alok: Toller Beitrag :).. acht Jahre alt, aber es klingt immer noch wahr

    Benutzer195488

    23. Dezember 2009 um 18:11 Uhr

Sie könnten (und sollten) den Fehler melden, aber in gewissem Sinne ist das Strom ist noch geschlossen:

Nach dem Aufruf von fclose() führt jede Verwendung von stream zu undefiniertem Verhalten.

  1. fclose() löscht alle ungeschriebenen Ausgaben (via fflush()) vor der Rückgabe, sodass der Fehler aus dem zugrunde liegenden resultiert write() wird nicht gemeldet fwrite() oder fprintf() Zeit, aber wenn Sie das tun fclose(). Als Ergebnis wird jeder Fehler, dass write() oder fflush() erzeugen kann erzeugt werden durch fclose().

  2. fclose() werde auch anrufen close() was auf NFS-Clients zu Fehlern führen kann, wenn die geänderte Datei erst auf den Remote-Server hochgeladen wird close() Zeit. Wenn der NFS-Server abgestürzt ist, dann die close() wird scheitern, und so fclose() wird auch scheitern. Dies gilt möglicherweise für andere vernetzte Dateisysteme.

Sie müssen IMMER das Ergebnis von fclose() überprüfen

Angenommen, Sie generieren Daten. Sie haben alte Daten, die Sie fread() aus einer Datei, und führen Sie dann eine Verarbeitung der Daten durch, generieren Sie weitere Daten und schreiben Sie sie dann in eine neue Datei. Sie achten darauf, die alte Datei nicht zu überschreiben, weil Sie wissen, dass der Versuch, eine neue Datei zu erstellen, fehlschlagen könnte, und Sie möchten in diesem Fall Ihre alten Daten behalten (einige Daten sind besser als keine Daten). Nach Abschluss aller fwrite()s, die alle erfolgreich sind (weil Sie den Rückgabewert von akribisch überprüft haben fwrite()), Sie fclose() die Datei. Dann Sie rename() Ihre gerade geschriebene Datei und überschreiben Sie die alte Datei.

Wenn fclose() aufgrund eines Schreibfehlers fehlgeschlagen (Festplatte voll?), Sie haben gerade Ihre letzte gute Datei mit etwas überschrieben, das Junk sein könnte. Hoppla.

Wenn es also kritisch ist, sollten Sie den Rückgabewert von überprüfen fclose().

In Bezug auf den Code:

#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;
}

Im obigen Code haben wir darauf geachtet fopen(), fwrite()und fread()aber selbst dann nicht überprüfen fclose() kann zu Datenverlust führen (bei Kompilierung mit MAYLOSE_DATA definiert).

Benutzer-Avatar
R. Samuel Klatschko

Ein Grund für fclose kann fehlschlagen, wenn noch Daten gepuffert sind und das implizite Flush fehlschlägt. Was ich empfehle, ist, immer fflush aufzurufen und dort die Fehlerbehandlung durchzuführen.

  • @Neil – die gleiche Fehlerbehandlung, die die App durchführen würde, wenn ein Schreibvorgang fehlschlägt (Anforderung fehlschlagen, Fehler protokollieren usw.).

    – R. Samuel Klatschko

    23. Dezember 2009 um 20:30 Uhr

1381610cookie-checkÜberprüfung des Rückgabewerts von fclose

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy