readsome und eof()



  • Hallo zusammen,

    ich würde gerne eine komplette Datei einlesen und von den Exceptions Gebrauch
    machen. Deswegen kann ich read nicht verwenden. Es setzt nämlich das failbit,
    wenn der Puffer nicht komplett gelesen wurde und somit flöge die Exception.

    Mein Ansatz sieht nun so aus:

    f.open(filename, std::ios::in);
    f.exceptions (std::ifstream::failbit | std::ifstream::badbit );
    while(f){ // !f.eof() geht auch nicht
    	f.readsome(buffer, 4096);
    	int i = f.gcount();
    	...
    }
    

    Jetzt terminiert meine Schleife aber nicht, da (scheinbar) kein eofbit gesetzt
    wird. Was kann ich tun um herauszufinden, ob ich am Ende des Streams angekommen
    bin?

    Gruß,
    XSpille

    EDIT: Bitte keine "Verwende-kein-readsome"-Lösung oder eine "Dateigrößen-basierte"-Lösung



  • Verwende mal RAII. Filename und Öffnungsmethode im Konstruktor übergeben.



  • 314159265358979 schrieb:

    Verwende mal RAII. Filename und Öffnungsmethode im Konstruktor übergeben.

    Dann erläuter mir mal bitte, warum der gepostete Code kein RAII verwendet...

    http://de.wikipedia.org/wiki/RAII

    Dabei wird die Steuerung der Belegung und der Freigabe von Betriebsmitteln (auch Ressourcen genannt) an den Gültigkeitsbereich von Variablen gekoppelt.

    Ich denke das ist bei diesem Code der Fall... 🙄

    Dann werd ich dir mal einen guten Grund verraten, warum ich das file nicht
    im Konstruktor öffnen lasse...

    Eigentlich sollten diese beiden Zeilen vertauscht sein.

    f.exceptions (std::ifstream::failbit | std::ifstream::badbit );
    f.open(filename, std::ios::in);
    

    Jetzt ist dir evtl. klar, welchen Vorteil es hat.
    So fliegt ne Exception, wenn beim Öffnen etwas schief geht.

    Ja... Ansonsten hätte man die Sachen im Konstruktor übergeben können,
    jedoch ein Verzicht darauf (wie ich denke) nichts mit einem Verstoß gegen RAII zu tun...

    Nenn wir einen Vorteil von deiner vorgeschlagenen Version...
    (abgesehen davon, dass du eine Zeile sparst)



  • Hmm...mit readsome wird das schwierig. Readsome liest nur aus dem mit dem Stream verbundenen streambuf-Objekt aus, was das schon lokal gespeichert hat, fordert aber von der darunterliegenden Datenquelle (der Datei) keine neuen Daten an. (Genau: Wenn rdbuf()->in_avail() == -1, wird eofbit gesetzt, vgl. 27.6.1.3 (30))

    Die Exception in deinem Fall wird wohl von gcount() geworfen, nachdem eofbit gesetzt wurde; in diesem Fall setzt gcount() failbit, und das ist in deiner Exception-Maske. Ich vermute, dass

    while(f.read(buffer, 4096)) {
      int i = f.gcount();
    }
    

    dein Problem löst. Sollte das nicht der Fall sein: kannst du ein kleines Testprogramm zusammenstellen, das das Problem reproduziert?



  • seldon schrieb:

    Kannst du ein kleines Testprogramm zusammenstellen, das das Problem reproduziert?

    #include <iostream>
    #include <fstream>
    
    int main(int argc, char** arg){
    
            char buffer[4096];
            std::ifstream f;
            f.exceptions (std::ifstream::failbit | std::ifstream::badbit );
            f.open(arg[1], std::ios::in);
            std::cout << arg[1] << std::endl;
            while(f){
                    f.readsome(buffer, 4096);
                    int i = f.gcount(); //Hier
                    std::cout.write(buffer, i); //Hier
            }
            std::cout << "File successfully read" << std::endl;
            return 0;
    }
    

    Die Schleife ist leider eine Endlos-Schleife!
    Wenn man die beiden markierten Zeilen auskommentiert und
    readsome in read ändert, hat man ja deinen Vorschlag, jedoch fliegt dann eine
    Exception:

    http://www.cplusplus.com/reference/iostream/istream/read/
    Hier steht auch, dass das flag von read gesetzt wird.

    Gruß,
    XSpille



  • Ah, Absatz übersehen. Ich dachte, read setzt nur eofbit, wenn das Ende der Datei erreicht ist, aber failbit wird da auch gesetzt. Folgendes funktioniert bei mir:

    #include <iostream>
    #include <fstream>
    
    int main(){
      char buffer[4096];
      std::ifstream f;
    
      f.exceptions (std::ifstream::failbit | std::ifstream::badbit );
      f.open(__FILE__);
    
      while(f.readsome(buffer, 4096)){
        int i = f.gcount();
        std::cout.write(buffer, i);
        std::cout.flush();
      }
    
      std::cout << "File successfully read" << std::endl;
    }
    

    ...aber ich bin mir nicht sicher, ob das überall der Fall sein muss. Die Definition von readsome greift auf Interfaces von basic_streambuf zurück, in denen ich nicht sonderlich firm bin. Wenn ich das richtig lese, darf (entgegen meiner früheren Vermutung) in_avail() nur -1 zurückgeben, wenn wirklich keine Zeichen mehr aus dem Stream gelesen werden können. Ich beziehe mich auf

    ISO/IEC 14882:2003 27.5.2.2.3 (1) schrieb:

    streamsize in_avail();

    Returns: If a read position is available, returns egptr(). Otherwise returns showmanyc() (27.5.2.4.3).

    Wobei egptr() das Ende des Stroms und gptr() die momentane Leseposition bezeichnet, das also -1 wäre, wenn gptr() gerade hinter egptr() liegt, d.h. EOF. showmanyc() ist definiert als

    ISO/IEC 14882:2003 27.5.2.4.3 (1, 6-7) schrieb:

    streamsize showmanyc();274)

    Returns: an estimate of the number of characters available in the sequence, or -1. If it returns a positive value, then successive calls to underflow() will not return traits::eof() until at least that number of characters have been extracted from the stream. If showmanyc() returns -1, then calls to underflow() or uflow() will fail.275)

    274) The morphemes of showmanyc are "es-how-many-see", not "show-manic".
    275) underflow or uflow might fail by throwing an exception prematurely. The intention is not only that the calls will not return eof() but that they will return ‘‘immediately.’’

    (...)

    int_type underflow();

    Notes: The public members of basic_streambuf call this virtual function only if gptr() is null or gptr() >= egptr()

    Returns: traits::to_int_type(c), where c is the first character of the pending sequence, without moving the input sequence position past it. If the pending sequence is null then the function returns traits::eof() to indicate failure.

    Demnach sollte das in Ordnung gehen. Aber, wie gesagt, ich habe mich mit diesem Teil des Standards bisher nicht sehr intensiv auseinandergesetzt, und es ist gut möglich, dass ich da etwas übersehe.



  • Danke seldon!

    seldon schrieb:

    Folgendes funktioniert bei mir:
    ...

    Ich denke nicht, dass es so funktioniert wie du denkst.
    Es liest nämlich solange bis der Puffer mal leer ist und das ist nicht
    (zwangsläufig) gleich dem Ende des streams.

    http://www.cplusplus.com/reference/iostream/istream/readsome/

    Return Value

    The number of characters extracted.

    ... readsome stops reading if the memory buffer associated with the stream runs out of characters, even if the End-Of-File has not yet been reached.

    Gruß,
    XSpille



  • Das Verhalten von std::ifstream::readsome() ist leider sehr stark abhängig von der Implementierung. Das ist einer der vielen tollen Stellen, an denen der Standard super schwammig ist. Schlußendlich hängt es von der Implementierung von std::filebuf::in_avail() ab. Wer schon mal versucht hat, auf verschiedenen Plattformen mit in_avail herauszubekommen, wieviele Zeichen noch in einem Input-Stream sind, kennt das Problem.

    Außerdem erschließt sich mir nicht, wieso Du hier Exceptions für das Erreichen des Dateiendes benutzen willst. Das Erreichen des Dateiendes ist doch keine Ausnahme, sondern die Regel. Du solltest weiterhin des Stream im Binär-Modus öffnen, wenn Du Rohdaten ausliest.

    PS: Es geht also doch auf "Verwende keine readsome-Lösung" hinaus. Es sei denn, Du willst Dich damit auseinandersetzen, wie Deine Plattform in_avail implementiert hat.



  • Hmm...jaaaa. Mehr oder weniger, jedenfalls. Wenn ich das richtig lese, darf readsome nur dann einen Fehlercode setzen, wenn das Ende des Datenstroms erreicht ist oder eh !good(), darf aber auf der anderen Seite in eine Endlosschleife geraten, wenn aus irgendeinem anderen Grund keine Zeichen mehr im Streambuffer sind bzw. der sie nicht zu besorgen weiß. Ich vermute, dass das für Sockets, FIFOs etc. gedacht ist.

    Wahrscheinlich käme man mit

    while(f.readsome(buffer, 4096), f) {
      // ...
    }
    

    in Bezug auf Dateien durch, aber es wäre wohl sinnvoll, eine Obergrenze für Lesefehler einzuführen. Beispielsweise

    std::size_t const max_fail = 100;
    std::size_t failures;
    std::streamsize read_chars;
    
    for(;;) {
      read_chars = f.readsome(buffer, 4096);
      if(f.eof()) { break; }
    
      if(read_chars == 0) { ++failures; }
      if(failures > max_fail) { f.setstate(std::ios::failbit); } // Exception triggern
    
      // Rest hier
    }
    

    ...und ich bin mir bewusst, dass das so ziemlich hässlich aussieht. Es wäre besser, den Kram in eine Funktion auszulagern, aber das lasse ich als Übungsaufgabe für den Leser. 😉

    Ansonsten könnte es Sinn machen, failbit aus der Exception-Maske rauszulassen und einfach mit read zu arbeiten.

    Oder, wenn es nur darum geht, die ganze Datei einzulesen:

    std::ostringstream bucket;
    bucket << f.rdbuf();
    std::string content = bucket.str();
    


  • Auf dem Compiler den ich hier im Moment zur Verfügung habe (MSVC9 SP1) gibt readsome im Falle das weniger als size Characters im Stream sind immer 0 zurück, und es wird auch nichts gelesen. Ich habe auch keine Ahnung, was ich machen muss, um dem entgegen zu wirken. Vielleicht äußert sich der TO mal dazu. Er könnte auch mal erklären, was er genau erreichen will.



  • Tachyon schrieb:

    Außerdem erschließt sich mir nicht, wieso Du hier Exceptions für das Erreichen des Dateiendes benutzen willst. Das Erreichen des Dateiendes ist doch keine Ausnahme, sondern die Regel. Du solltest weiterhin des Stream im Binär-Modus öffnen, wenn Du Rohdaten ausliest.

    Ich möchte keine Exception erreichen, sondern verhindern.
    EDIT: Aus diesem Grund wollte ich jetzt readsome statt read verwenden

    Also generell möchte ich einfach nur die zur Verfügung gestellten Exceptions
    nutzen.

    Leider setzt read, wenn es zurückkehrt ohne den buffer komplett gelesen zu haben,
    das failbit. Ich find das ziemlich dämlich, da das in meinen Augen
    keine Exception ist, auch wenn dadurch eine ausgelöst wird.

    http://www.cplusplus.com/reference/iostream/istream/read/

    If the End-of-File is reached before n characters have been read, the array will contain all the elements read until it, and the failbit and eofbit will be set (which can be checked with members fail and eof respectively).



  • Ich würde es an Deiner Stelle so machen, wie Seldon es vorschlägt: Exception für den Stream komplett aus schalten und im Bedarfsfall selbst werfen. Das mit readsome hat aus den genannten Gründen eher nur Nachteile.



  • Tachyon schrieb:

    Ich würde es an Deiner Stelle so machen, wie Seldon es vorschlägt: Exception für den Stream komplett aus schalten und im Bedarfsfall selbst werfen. Das mit readsome hat aus den genannten Gründen eher nur Nachteile.

    Danke euch beiden!
    Ich hab nur gedacht, es gäbe eventuell ne schöne Lösung um die integrierten
    Exceptions zu nutzen 😞


Anmelden zum Antworten