Frage zu istream_iterator



  • So lange nicht die Chance besteht dass es knallt wegen out-of-memory, is immer noch das beste alles in einem Rutsch einzulesen. Dann haste deine BiDi Iteratoren und alles ist gut. Alternativ gäbe es Memory-Mapping, dummerweise ist das plattformabhängig. (Gibt's da ne gute portierbare Dings, Boost oder so? Keine Ahnung.)

    Ansonsten, wenn du bloss CSV File parsen willst oder etwas ähnlich einfaches... der dafür nötige Automat ist recht schnell selbst geschrieben. Dann kannst du die Daten blockweise lesen (z.B. 1 MB Blöcke) und blockweise durch den Automaten stopfen.

    Was hälst du von der Idee einen Bidirektionalen istreambuf_iterator zu schreiben? Der müsste ja einfach die alten Puffer sammeln statt zu verwerfen.

    Ich hab von dem ganzen iostreams Jedöns zu wenig Ahnung um dazu fundiert was sagen zu können, aber ich stelle es mir aufwendig vor.

    Edit: Man müsste prüfen ob der stream seekable ist. Dann kann man zurück ohne Buffersammeln.

    Uiuiui, du willst ganz sicher nicht jedes mal wenn die Regex meint hüpfen zu müssen den Stream seeken. Ausserdem musst du noch bedenken dass du bei "normalen" (nicht "single pass input") Iteratoren auch unabhängige Iterator-Kopien unabhängig und korrekt behandeln musst. D.h. du müsstest vor dem Lesen jedes Zeichens nochmal den Stream-Offset prüfen und... also ne, ich würde das nicht implementieren wollen. Und performant is vermutlich auch was anderes.


    Falls du einfach nur gute Performance mit wenig Aufwand willst, dann würde ich sagen probier's erstmal mit getline. Guck erstmal wie viele MB/s du damit hinbekommst und ob das reicht. Und falls es dir langsam vorkommt guck noch in welchen Funktionen wie viel Zeit draufgeht (Profiler) - nicht dass der Flaschenhals am Ende noch das Parsen der bereits in den std::string geladenen Daten ist und du fängst an was zu optimieren was gar nicht so bremst.

    Falls es dir natürlich darum geht dich damit zu spielen und dazuzulernen ist das was anderes, in dem Fall mach ruhig und probiere & teste wozu du lustig bist. Trotzdem würde ich auch da empfehlen dir auch die einfache Lösung anzusehen, damit du nen Vergleich hast.



  • @hustbaer Ok ok. Dachte zum üben in Sachen iteratoren... Aber dann fange ich besser mit einfacheren an. Im Breymann ist was zum lesen.

    Für diesen CSV-artigen Zeilenaufbau aus dem Post reicht natürlich getline().
    Aber ich habe auch an eine Hilfsklasse gedacht die die >>-operatoren überlädt (Und umgekehrt zum schreiben). Irgendwer hat mir mal (möglicherweise du) vor längerer Zeit zu solch einer Klasse geraten im Zusammenhang mit dem lesen von BMP-Dateien. Ist bestimmt ein Jahr her. Jemand wollte BMP-Dateien aufdröseln und ich hatte da auch was in der Richtung probiert. Moment... such ...

    // $Id: binaryreader.h 335 2017-11-29 01:42:03Z dirk $
    //
    
    #ifndef BINARY_IFSTREAM_H
    #define BINARY_IFSTREAM_H
    
    #include <cstdint>
    #include <istream>
    #include <type_traits>
    
    namespace zeugs {
      namespace iohelper {
    
        // Hilfsklasse um die Operatorfunktion>> für integrale Datentypen
        // auf binäres Verhalten zu ändern. Mit z.B. bstream >> intVar
        // werden sizeof(intVar) Bytes gelesen und nach intVar geschrieben.
        // Awendungsbeispiel:
        // int intVar;
        // BinaryReader br( std::ifstream( "test.bmp", std::ios::binary|std::ios::in ) )
        // br >> intVar;
    
        class BinaryReader
        {
          std::istream &is;
        public:
          BinaryReader(std::istream &is) : is(is) { }
    
          // Operator>> nur für integrale Typen:
          template <class T, class = std::enable_if<std::is_integral<T>::value> >
          BinaryReader & operator>> (T& value)
          {
            is.read(reinterpret_cast<char*>(&value), sizeof(T));
            return *this;
          }
    
          inline std::istream & get_stream()
          {
            return is;
          }
        };
     }
    }
    

    Tja, sowas als csv_helper. Kann man dann noch den delimiter setzen oder auch quote-Zeichen. Dann einfach:

    csv_helper csvh(std::cin);
    
    std::string text;
    int ganzzahl;
    float gk_zahl;
    
    csvh >> text >> ganzzahl >> gk_zahl;
    

    Bei Eingabe: foobar, 42, 3.14 (Problem, Text darf kein Komma enthalten)

    Oder:

    csv_helper csvh(std::cin);
    csvh.setDelim(";");
    csvh.setQuote("\"\"");
    
    std::string text;
    int ganzzahl;
    float gk_zahl;
    
    csvh >> text >> ganzzahl >> gk_zahl;
    

    Eingabe: "foobar"; "42"; "3.14"

    Sowas in der art...

    Ich wollte ja noch die istream::read()-Version testen. Nur wie finde ich die Dateigröße heraus bei einem geöffneten ifstream. std::filesystem::file_size geht hier glaub nicht (zumindest funktioniert ...::exists() nicht). Hab zwar einen g++-8 aber noch die libstdc++ V6 😕 Wegen Datei an einem Stück einlesen. Bei meinen Testdateien kenne ich die Größe ja. Aber so generell...

    cu



  • Grösse rausbekommen ist leider wirklich lästig. Was geht ist ans Ende zu springen und sich dann die Position zu holen, und danach wieder zurückzuspringen - seekg/tellg. Scheint "common practice" zu sein. Dass es dazu keine "direkte" C oder C++ API gibt ist doof. Vermutlich dem Umstand geschuldet dass es auch bei den FILE* Funktionen nix vergleichbares gibt. Andrerseits könnte es das OS, zumindest für "normale" Files...



  • @hustbaer sagte in Frage zu istream_iterator:

    Grösse rausbekommen ist leider wirklich lästig. Was geht ist ans Ende zu springen und sich dann die Position zu holen, und danach wieder zurückzuspringen - seekg/tellg. Scheint "common practice" zu sein. Dass es dazu keine "direkte" C oder C++ API gibt ist doof. Vermutlich dem Umstand geschuldet dass es auch bei den FILE* Funktionen nix vergleichbares gibt. Andrerseits könnte es das OS, zumindest für "normale" Files...

    Jo über entsprechende "stat" funktionionen. Wobei mit boost::filsystem oder seit c++17 std::filesystem gibt es auch "stat" ähnliche funktionen.
    z.b. für Dateigröße in bytes https://www.boost.org/doc/libs/1_51_0/libs/filesystem/doc/reference.html#file_size



  • Nur is "stat" halt doof, was du bräuchtest wäre "fstat".
    Per Pfad nochmal auf das File zugreifen nachdem man es aufgemacht hat ist halt schlecht, weil's ne schöne Race-Condition erzeugt. Was wenn das File dazwischen umbenannt oder gelöscht wurde?

    Also Richtlinie: File über den Pfad aufmachen und danach alles nur mehr über das File-Handle machen.



  • @dirkski : Nur wofür brauchst du denn die Größe? Lies einfach bis zum Ende (das ist doch der Sinn eines Iterators).



  • @hustbaer sagte in Frage zu istream_iterator:

    Nur is "stat" halt doof, was du bräuchtest wäre "fstat".

    In der C-API gibt es ein fstat, zu mindestens unter linux https://linux.die.net/man/2/stat
    Wobei das nicht mit einem FILE* handle funktioniert.



  • @firefly: Da fehlt eine Leerzeile in deinem Beitrag.

    Ich finde es auch nervig, daß man nach dem Zitieren diese Leerzeile einfügen muß.
    Die Leerzeile ist aber wohl beim Zitieren da, nur daß der Cursor nicht in der letzten Zeile steht, sondern direkt in dieser Leerzeile (man kann nämlich mit "Cursor down" eine Zeile nach unten wandern).

    Edit:
    Habe dazu mal einen Beitrag in der Forentechnik erzeugt: Cursor nach Zitieren an falscher Position



  • @th69 sagte in Frage zu istream_iterator:

    @dirkski : Nur wofür brauchst du denn die Größe? Lies einfach bis zum Ende (das ist doch der Sinn eines Iterators).

    Ja natürlich. Es ging aber hier speziell darum die Datei in einem Rutsch in einem passend mit reserve vergrößerten std::string zu speichern. Ansonsten hast du natürlich Recht. Ich wollte nur die Zeiten messen, daher.

    std::filesystem::file_size funktioniert hier nicht, hab noch eine alte libstdc++. hab ich ehrlich nicht getestet, aber es gab bei std::filesystem::exists schon linker-fehler. Auch sollte es nur mit reinen std-c++ gehen, also ohne boost oder fstat.

    Das ist aber nicht so wichtig, ging nur ums benchmarking...

    Evtl. hab ich oben noch ein Fehler. Genauer bei einer Änderung, hier nicht gezeigt.

    In der Änderung vergrößere ich den string auf count+1. Weil nach den 100000 'x'se ja noch ein newline kommt. Bei mir klappt das ja, Aber ist unter Windows das linefeed nicht 2 Byte groß? (CR+NL, oder umgekehrt, Schreibmaschinensimulation halt)

    [...]
    int main( int, char**)
    {
      int rounds=5;
      long count = 100000;
      long s_size = count+1;
      std::array<std::string, 2> files{"test1.txt", "test2.txt"};
    
    [...]
        std::string s; s.reserve(s_size);
          if (!std::getline(ifs, s))
            exit (EXIT_FAILURE);
    [...]
    

    Muss ich extra die Länge von "\n" messen um das rauszukriegen? Oder gibts da eine Konstante irgendwo. Nur std-c++ natürlich.

    cu



  • @hustbaer sagte in Frage zu istream_iterator:

    Nur is "stat" halt doof, was du bräuchtest wäre "fstat".
    Per Pfad nochmal auf das File zugreifen nachdem man es aufgemacht hat ist halt schlecht, weil's ne schöne Race-Condition erzeugt. Was wenn das File dazwischen umbenannt oder gelöscht wurde?

    Also Richtlinie: File über den Pfad aufmachen und danach alles nur mehr über das File-Handle machen.

    Ja, aber stat/fstat kommt hier nicht in Frage. Blöd nur das es auch für std::filesystem::file_size gilt. So wie ich das sehe.



  • @dirkski für std::filesystem support muss man unter linux mit dem gcc gegen stdc++fs linken.



  • @firefly sagte in Frage zu istream_iterator:

    @dirkski für std::filesystem support muss man unter linux mit dem gcc gegen stdc++fs linken.

    Jep. Nur hab ich die nicht. Auch nicht im repo:

     5 | devel_gcc                 | GNU Compiler Collection container (openSUSE_Leap_42.3)             | Yes     | (r ) Yes  | No     
    

    Ich muss erst meine suse upgraden, das wiederum geht nicht weil meine Internetanbindung (die normalerweise schon schlecht ist) jetzt quasi nicht mehr funzt. Seit fast 2 Wochen. Normal ist LTE, jetzt nur GPRS 😕 Bin heil froh das ich die cpp-reference hier lokal installiert habe...

    Ich sag jetzt besser nicht wie gut das Forum damit funktioniert.

    Edit: ich habe mit 'ldconfig -p | grep ...' und 'zypper --no-refresh se ...' gesucht, nix

    dirk@schleppi:~/develop/cpp/test/csv_helper> /sbin/ldconfig -p | grep c++
            libstdc++.so.6 (libc6,x86-64) => /usr/lib64/libstdc++.so.6
    

    Noch die alte V6. Die war noch ohne fs. Mein ich, glaub ich. g++ ist aber V8. stdc++ ist glaub V7 aktuell...

    cu



  • So, das eigentliche Thema ist gelöst. std::istreambuf_iterator ist nicht Bidirektional und geht daher nicht mit regex. Falls noch jemand eine Antwort auf die letzte Frage hat (Windows-newline) währe ok, muss aber nicht.

    Eigentlich kann ich das hier jetzt als [solved] markieren, oder?

    cu



  • @dirkski sagte in Frage zu istream_iterator:

    Bei mir klappt das ja, Aber ist unter Windows das linefeed nicht 2 Byte groß? (CR+NL, oder umgekehrt, Schreibmaschinensimulation halt)

    Ja, Windows macht \r\n aka. CR-LF.
    Bei Code der schnell laufen soll hab ich es bisher immer so gemacht alles im "binary" Mode einzulesen und dann den Code der die Daten verarbeitet so angepasst dass er mit allen Varianten klarkommt.



  • @hustbaer sagte in Frage zu istream_iterator:

    @dirkski sagte in Frage zu istream_iterator:

    Bei mir klappt das ja, Aber ist unter Windows das linefeed nicht 2 Byte groß? (CR+NL, oder umgekehrt, Schreibmaschinensimulation halt)

    Ja, Windows macht \r\n aka. CR-LF.
    Bei Code der schnell laufen soll hab ich es bisher immer so gemacht alles im "binary" Mode einzulesen und dann den Code der die Daten verarbeitet so angepasst dass er mit allen Varianten klarkommt.

    Ok, Danke. Also ist '\n' unter Win auch ein Zeichen. Dann brauche ich den Code nicht zu ändern, oder. Hmm, oder erzeugt Win bei "fobar\n" im code intern ein "\r\n". Naja, ist nicht so wichtig. Wollte nur wenn ich schon code ins Forum blase der auch überall läuft... Werde den Puffer einfach auf +2 ändern und oben beim 2. Benchmark-Ergebnis den fertigen Code posten. Nein ich wollte ja noch die read-variante hinzufügen. Dann poste ich das nochmal.

    cu



  • @dirkski sagte in Frage zu istream_iterator:

    @firefly sagte in Frage zu istream_iterator:

    @dirkski für std::filesystem support muss man unter linux mit dem gcc gegen stdc++fs linken.

    Jep. Nur hab ich die nicht. Auch nicht im repo:

     5 | devel_gcc                 | GNU Compiler Collection container (openSUSE_Leap_42.3)             | Yes     | (r ) Yes  | No     
    

    Ich muss erst meine suse upgraden, das wiederum geht nicht weil meine Internetanbindung (die normalerweise schon schlecht ist) jetzt quasi nicht mehr funzt. Seit fast 2 Wochen. Normal ist LTE, jetzt nur GPRS 😕 Bin heil froh das ich die cpp-reference hier lokal installiert habe...

    Ich sag jetzt besser nicht wie gut das Forum damit funktioniert.

    Edit: ich habe mit 'ldconfig -p | grep ...' und 'zypper --no-refresh se ...' gesucht, nix

    dirk@schleppi:~/develop/cpp/test/csv_helper> /sbin/ldconfig -p | grep c++
            libstdc++.so.6 (libc6,x86-64) => /usr/lib64/libstdc++.so.6
    

    Noch die alte V6. Die war noch ohne fs. Mein ich, glaub ich. g++ ist aber V8. stdc++ ist glaub V7 aktuell...

    cu

    Ich glaube du hast da was missverstanden. stdc++fs ist nicht bestandteil der libstdc++.so sondern ist eine separate library (AFAIK aktuell eine static lib *.a). Und diese library ist bestandteil der installation des compilers.
    Da du gcc 8.x installiert hast sollte diese library auch auf deinem system vorhanden sein.

    Beim linken einfach -lstdc++fs angeben.



  • @firefly Jaaa, gefunden. Schwere Geburt. Vielen Dank.

    Erstmal Pause. Zuviel gegessen. Burps.



  • @dirkski sagte in Frage zu istream_iterator:

    Hmm, oder erzeugt Win bei "fobar\n" im code intern ein "\r\n".

    Die String-Literals bleiben schon so wie sie da stehen, "foo\n" ist überall 4 Zeichen lang (5 wenn man die terminierende NUL mitzählt). Es gibt allerdings Magic in den FILE* und iostreams Funktionen wo \r\n zu nur \n übersetzt wird und umgekehrt. Allerdings nur wenn man die Files im "default" (=text) mode aufmacht und nicht im "binary" mode. Welche APIs davon genau betroffen sind und welche nicht weiss ich leider nicht auswendig.

    Nicht betroffen sind auf jeden Fall die seek/tell Funktionen. Sonst müsste seek ja alles lesen was zwischen "hier" und "dort" ist um zu ermitteln wie viele ignorierte "\r" enthalten sind und "abgezogen" werden müssen.

    Alles in allem bin ich kein grosser Fan dieser "automatischen" Übersetzung, weshalb ich im Normalfall nicht damit arbeite.



  • @hustbaer Ok, danke. Wäre das auch geklärt...



  • Benutzt eigentlich überhaupt jemand die istream_iterator und istreambuf_iterator? Also kann man die wirklich für etwas gebrauchen, wo es nicht eine einfachere/effizientere Alternative gibt?


Anmelden zum Antworten