C++ Datei schließen ?



  • Hallo Leute mal eine Frage bzgl. Dateiarbeit... In vielen Büchern und Tutorials wird immer wieder Datei.close() verwendet.
    Nun bin ich mir nicht mehr ganz sicher, aber ich wage zu behaupten hier im Forum mal gelesen zu haben, das es eigentlich keine .close() braucht, da dies durch den Gültigkeitsbereich selbst erledigt wird?

    Meine Frage nun, wann brauche in ein .close und wann nicht ?

    #include <iostream>
    #include <fstream>
    
    void foo(){
    
    std::ofsteam file("test.txt", std::ios_base::app);
    
    //do sth. with file
    
    file.close()  // braucht es nicht, da file am Ende von foo "zerstört" wird oder?
    }
    

    ... oder sollte man sicherheitshalber einfach mal ein .close setzten ?

    Wie siehts bei einer Klasse aus, die im Konstruktor eine Datei öffnet, muss ich die im Destruktor selbst schließen oder läuft das so mit ?



  • Der Destruktor von std::ofstream erledigt den close(), wenn er ausgeführt wird.



  • Ah, die alte "ist file.close() zu was gut" Frage.

    file.close() wegzulassen ist grundsätzlich eine Unart. Der Nachteil beim Weglassen von file.close() wird bloss dadurch "maskiert", dass iostreams per Default so eingestellt sind dass bei Fehlern keine Exceptions geworfen werden.

    iostreams tun nämlich Daten puffern. Was zur Folge hat dass close wesentlich mehr machen kann als bloss Speicher bzw. allgemein Resourcen freigeben. Nämlich die letzten paar Daten, die noch gepuffert sind und noch nicht geschrieben wurden, rausschreiben, bevor das File dann geschlossen wird. Was fehlschlagen kann. Was man normalerweise mitbekommen möchte.

    =>
    Entweder Exceptions auf dem ostream aktivieren und vor Programmende selbst close aufrufen.
    Oder vor Programmende flush aufrufen und dann nochmal den Stream-State prüfen.

    Nur close ohne aktive Exceptions bringt natürlich wirklich nichts. Dann kann man es genau so gut gleich weglassen. Was mMn. aber die schlechteste aller Lösungen ist.



  • hustbaer schrieb:

    file.close() wegzulassen ist grundsätzlich eine Unart.

    Nö.
    Das Problem ist grundsätzlich ungelöst. Was machste mit zwei Files in zwei unterschiedlich tiefen Funktionen? close() muss einfach klappen.



  • Oder vor Programmende flush aufrufen und dann nochmal den Stream-State prüfen.

    ? So :

    file.flush;
    
    if (!file.fail){
         file.close();
    }
    

    ?? Oder wie meinst du das?



  • volkard schrieb:

    hustbaer schrieb:

    file.close() wegzulassen ist grundsätzlich eine Unart.

    Nö.
    Das Problem ist grundsätzlich ungelöst.

    Nö.

    volkard schrieb:

    Was machste mit zwei Files in zwei unterschiedlich tiefen Funktionen?

    Beim tieferen (inneren) close() fliegt ne Exception.
    Dadurch wird das äussere File vom Dtor geschlossen. Was effektiv ein "abort" ist, also ein "versuchs bitte aber ignorier' alle Fehler" Schliessen.
    Der Inhalt beider Files ist potentiell kaputt. Aber - sofern das OS kein kompletter Schrott ist: beide Handles wurden freigegeben, beide Files sind also "geschlossen".

    Irgendwo wird die Exception gefangen und dem User angezeigt. Jetzt weiss er dass die Files möglicherweise Schrott enthalten.

    => Problem gelöst.

    volkard schrieb:

    close() muss einfach klappen.

    Und was wenn nicht? Geht dann die Welt unter?
    => Unsinn.

    Es geht nicht darum zu garantieren dass immer alles gut geht. Das kann man nicht. Es geht darum zu garantieren dass man nur dann "haben fertig, alles OK" sagt, wenn auch wirklich alles OK ist. Und das ist nicht grundsätzlich ungelöst, sondern sogar sehr einfach zu lösen. Nur halt nicht "sehr einfach" mit den iostreams, weil sie den falschen Default-Wert für die Exceptions-Maske haben.



  • cpp_beginner_offl schrieb:

    ? So :

    file.flush;
    
    if (!file.fail){
         file.close();
    }
    

    ?? Oder wie meinst du das?

    Nein.
    Eher so:

    file.flush();
    if (!file)
        throw std::runtime_error("Waaaaaaaah, MIST!");
    

    Bzw. auch gerne als

    if (!file.flush())
        throw std::runtime_error("Waaaaaaaah, MIST!");
    

    geschrieben.

    Nach dem flush() kann man das File dann ruhig über den Dtor schliessen lassen. Alle Änderungen wurden ja bereits geschrieben, d.h. es gibt im Dtor nix mehr zu tun was schief gehen könnte.
    Oder anders gesagt: es können keine Fehler mehr passieren von denen man erfahren wollen würde.



  • Ich hab mal kurz in der Onlinerefernz zu flush nachgelesen...

    http://www.cplusplus.com/reference/ostream/ostream/flush/

    Ok, Return Value *this, darum prüfst du auf (!file)...
    Es steht aber, wenn ich auf die schnelle richtig übersetzt habe , auch drinn, das verschiedene Failstates gesetzt werden.

    Würde

    if (file.fail() ){
        throw std::runtime_error ("Sth. really bad happened...");
    }
    

    genauso funktionieren oder ist es "sicherer" auf (!file) zu prüfen ?



  • Laut http://www.cplusplus.com/reference/ios/ios/operator_not/ ist !stream equivalent zu fail()



  • Hab selbst grad noch ein wenig nachgelesen/rumprobiert.
    Noch besser wäre gleich close aufzurufen und danach den Stream-State zu prüfen.
    Also

    file.close(); // liefert nicht den Stream zurück, geht daher nicht direkt im if
    if (!file)
        throw std::runtime_error("Waaaaaaaah, MIST!");
    

    close setzt nämlich im Falle des Falles auch das fail Bit, und das Prüfen des Stream-States scheint auch nach dem Schliessen des Files erlaubt zu sein.
    => Besser als die flush() Variante, da man keine Vermutung mehr darüber anstellen muss ob close nach erfolgreichem flush noch fehlschlagen kann/darf oder nicht.


  • Mod

    Dir ist schon klar, dass jetzt viele Leute diesen Code wortwörtlich kopieren werden und dann stolz meinen werden, dass

    throw std::runtime_error("Waaaaaaaah, MIST!");
    

    eine bessere Art der Fehlerbehandlung wäre, als das ganze Thema einfach zu ignorieren und gar kein close zu setzen?



  • Also wer die Fehlermeldung 1:1 übernimmt, dem kann ich auch nicht helfen.

    Gibt es vom Text "Waaaaaaaah, MIST!" abgesehen noch etwas zu kritisieren?

    ps: Schlag ne bessere Fehlermeldung vor, und ich kopier' sie rein (bzw. fühl dich frei es selbst reinzueditieren wenn du es für wichtig bzw. sinnvoll erachtest).


  • Mod

    hustbaer schrieb:

    Also wer die Fehlermeldung 1:1 übernimmt, dem kann ich auch nicht helfen.

    Gibt es vom Text "Waaaaaaaah, MIST!" abgesehen noch etwas zu kritisieren?

    Ich meine eher, dass das reine Werfen der Exception in diesem Fall nicht viel bringt, wenn sie nicht auch sinnvoll behandelt wird. Was bringt mir eine ungefangene Exception und Datenverlust gegenüber einfach nur Datenverlust?



  • Hm.
    Verstehe ich jetzt nicht ganz wieso du das als Problem siehst.

    Ich bin schon der Meinung dass es deutlich besser ist wenn ein Programm im Fehlerfall mit einer Fehlermeldung der CRT bzw. des OS abbricht, als wenn es im Fehlerfall "OK, haben fertig, alles gut" hinschreibt.
    Du nicht?
    Ich finde falsche Erfolgsmeldungen halt schon sehr schlimm.

    Natürlich ist es meistens besser die Exception irgendwo zu fangen. Nur wie und wo man Exceptions sinnvoll behandelt würde hier mMn. deutlich den Rahmen sprengen. Und einfach nur ein try-catch um die ganze Show drumrumschreiben würde das Beispiel wohl kaum signifikant verbessern. Da das im Falle des Falles dann auch 1:1 mitkopiert wird, auch wenn es in dem Programm wo es reinkopiert wird an der Stelle wo es dann landet total deplaziert ist.

    Mir ging es lediglich darum eine bessere Alternative zu " close einfach weglassen" aufzuzeigen. Vor allem da man leider so oft Tips wie " close brauchst du nicht" lesen muss. (Was für ifstream ja OK ist, aber halt nicht für ofstream .)

    ps:
    Wäre es deiner Meinung nach besser in solchen Beispielen etwas ala

    file.close(); // liefert nicht den Stream zurück, geht daher nicht direkt im if 
    if (!file)
    {
        // TODO: Fehler behandeln
    }
    

    zu schreiben?
    Oder hast du nen anderen, besseren Vorschlag?
    Aufruf einer im Beispiel nicht definierten Funktion mit beschreibendem Namen ala DateiSchreibeFehlerBehandeln() vielleicht? Damit der Code nach 1:1 copy+paste wenigstens nicht compiliert?


  • Mod

    Ich hätte es halt besser gefunden, wenn du ein bisschen auf mögliche Reaktionen auf den möglichen Fehler eingegangen wärst, denn

    }
    

    = schlecht

    und

    file.close(); 
      if (!file)
        throw std::runtime_error("Waaaaaaaah, MIST!");	
    }
    

    = besser

    trifft es nicht wirklich. Da fehlt zu viel Drumherum, um dem Niveau der Frage gerecht zu werden.



  • SeppJ schrieb:

    Da fehlt zu viel Drumherum, um dem Niveau der Frage gerecht zu werden.

    Welches Drumherum, das direkt mit der Frage des OP in Zusammenhang steht, geht dir ab?

    Ich finde es auch einigermassen anstrengend dass du nicht sagst was dir eigentlich nicht passt/abgeht/.... Und zu guter Letzt: wenn etwas wichtiges fehlt, dann ergänze es doch einfach.


  • Mod

    Ich kann nicht sagen, was da fehlt, ist schließlich deine Antwort. Du antwortest auf dem Niveau professioneller Entwicklung, wo Datenkonsistenz sicher gestellt werden muss, erklärst aber nur, wie du einen eventuellen Datenverlust überhaupt feststellst. Der Fragesteller will hingegen vermutlich eher wissen, ob seine Datei überhaupt zu geht. Deine Antwort sieht so aus, als solle der Fragesteller den Code

    file.close();
      if (!file)
        throw std::runtime_error("Waaaaaaaah, MIST!");
    

    in seinen Anfängerprogrammen übernehmen und sie würden dadurch automatisch irgendwie besser dadurch, dass er nun eine ungefangene Exception erhält, wenn ihm irgendjemand den Datenträger abzieht, während er "Hello World" in eine Datei schreiben möchte.



  • SeppJ schrieb:

    Ich kann nicht sagen, was da fehlt, ist schließlich deine Antwort. Du antwortest auf dem Niveau professioneller Entwicklung, wo Datenkonsistenz sicher gestellt werden muss, erklärst aber nur, wie du einen eventuellen Datenverlust überhaupt feststellst. Der Fragesteller will hingegen vermutlich eher wissen, ob seine Datei überhaupt zu geht. Deine Antwort sieht so aus, als solle der Fragesteller den Code

    file.close();
      if (!file)
        throw std::runtime_error("Waaaaaaaah, MIST!");
    

    in seinen Anfängerprogrammen übernehmen und sie würden dadurch automatisch irgendwie besser dadurch, dass er nun eine ungefangene Exception erhält, wenn ihm irgendjemand den Datenträger abzieht, während er "Hello World" in eine Datei schreiben möchte.

    Sorry Sepp, du vermutest zu viel.
    Die Erklärung von hustbaer fand ich jetzt nicht schlecht. Es geht mir nicht darum in Erfahrung zu bringen ob meine Datei geschlossen wird, sondern wie im Anfangsthread schon geschreiben, wann ein .close sinnvoll ist oder eben nicht usw usw.

    Ich meine eher, dass das reine Werfen der Exception in diesem Fall nicht viel bringt, wenn sie nicht auch sinnvoll behandelt wird. Was bringt mir eine ungefangene Exception und Datenverlust gegenüber einfach nur Datenverlust?

    Das jmd. mitbekommt das jetzt was schiefgegangen ist ?



  • hustbaer schrieb:

    Hab selbst grad noch ein wenig nachgelesen/rumprobiert.
    Noch besser wäre gleich close aufzurufen und danach den Stream-State zu prüfen.
    Also

    file.close(); // liefert nicht den Stream zurück, geht daher nicht direkt im if
    if (!file)
        throw std::runtime_error("Waaaaaaaah, MIST!");
    

    close setzt nämlich im Falle des Falles auch das fail Bit, und das Prüfen des Stream-States scheint auch nach dem Schliessen des Files erlaubt zu sein.
    => Besser als die flush() Variante, da man keine Vermutung mehr darüber anstellen muss ob close nach erfolgreichem flush noch fehlschlagen kann/darf oder nicht.

    Jetzt hast du aber wieder alle Nachteile des manuellen Aufräumens. Stell dir mal vor, eine andere Exception (die später gefangen wird und der User vermutet nicht, dass die Datei fehlerhaft ist) wird geworfen. Dann ists vorbei mit deiner Aufräumaktion. Wenn also nichts anderes dagegenspricht, sollte man denke ich direkt die Exceptionmaske für den ofstream richtig setzen und auf close() verzichten.



  • m.e. schrieb:

    Stell dir mal vor, eine andere Exception (die später gefangen wird und der User vermutet nicht, dass die Datei fehlerhaft ist) wird geworfen.

    Diesen Satz verstehe ich nicht. Kannst du das in (Pseude-)Code skizzieren?

    m.e. schrieb:

    Wenn also nichts anderes dagegenspricht, sollte man denke ich direkt die Exceptionmaske für den ofstream richtig setzen und auf close() verzichten.

    Soll mir auch Recht sein.
    Mir ist hauptsächlich wichtig dass die Leute wissen warum es ein Fehler ist einfach nix zu machen.


Log in to reply