Einlesen eines Files und davor dessen Zeilenanzahl bestimmen



  • Habe das folgendermaßen gelößt

    std::map<std::string, std::vector<std::vector<float>>> pointClouds_;
      std::ifstream inputFile{};
      std::string line{};          
      std::istringstream istream{};
      std::vector<float> Point;
      for (auto const &filename : FileNames_) {
        if (filename.find("Image") != std::string::npos) continue;
    
        inputFile.open(filename);
        if (!inputFile.is_open() || inputFile.fail()) continue;
        for (size_t i = 1; i <= 11; ++i) {  // ignore the first 11 line
          inputFile.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
        }
        int lines_count = std::count(std::istreambuf_iterator<char>(inputFile),
                                     std::istreambuf_iterator<char>(), '\n');
    
        while (getline(inputFile, line)) {
          if (line.empty()) continue;  // Ignore empty lines
          istream.str(line);
          std::copy(std::istream_iterator<float>(istream),
                    std::istream_iterator<float>(), back_inserter(Point));
          pointClouds_[filename].push_back(Point);
          istream.clear();
          Point.clear();
        }
        inputFile.close();
      }
    

    läuft soweit auch, allerdings nur wenn ich die Zeile mit std::count auskomentiere. Habe dazu ein paar Fragen

    • Wieso saugt mir der count algorithmus den ifstream leer? und wie wäre hier die richtige Lösung um vorab schon die Zeilenanzahl im File zu bestimmen, damit ich ensprechend die Container Kapazität erhöhen kann um Umkopierne zu verhindern?
    • ist die Deklaration der Variablen über der Loop sinnvoll und das anschließende clearen oder hier lieber im Schleifenrumnpf immer die Objekte neu delarieren?
    • ist "ignore" hier die richtige Wahl um die ersten 11 Zeilen zu skippen?
    • ist std::copy hier die richtige Wahl um einzelne floats aus nem string in einen vector zu schreiben?

    Danke für eure Hilfe


  • Mod

    Natürlich saugt dir count den Stream leer, es muss schließlich alles von vorne bis hinten durchgehen. Das heißt, nach dem count bist du am Ende der Daten. Wenn das eine Datei ist, hindert dich aber niemand, wieder anderswo hin zu springen. Der Stream wird natürlich auch im eof-Fehlerzustand sein, der vorher entsprechend zurückgesetzt werden muss.

    Das ganze Vorhaben ist Blödsinn. Du willst etwas optimieren, dass praktisch keine Zeit braucht (Das dynamische Wachsen eines Vectors), dafür machst du aber das, was mit Abstand der Zeitaufwändigste Teil der Aufgabe ist (Lesen und Verarbeiten von Daten) doppelt.



  • Danke schonmal. Also das mit der Zeilenanzahl und rerserve lassen, hab ich verstanden.

    Und was ist mit meinen anderen Punkten?



  • Das ignore geht so in Ordnung würde ich sagen. Zumindest wenn die Anzahl der zu ignorierenden Zeilen und / oder die Länge der Zeilen variabel ist. Wenn du die Anzahl der Zeilen und Zeichen pro Zeile kennst, könntest du mit seekg() direkt zum richtigen Zeichen springen.

    Ich würde die Variablen immer erst da deklarieren wo ich sie brauche. Also im inneren der Schleife. Das macht den Code meiner Meinung nach besser zu lesen. Optimieren wird das dein Compiler schon für dich.

    std::copy habe ich so noch nicht gesehen. Du könntest wohl aus die assign Funktion von std::vector verwenden, oder wenn du ihn erst da deklarierst könnte auch sowas gehen:

    std::vector<float> Point(std::istream_iterator<float>(istream), std::istream_iterator<float>());
    


  • Schlangenmensch schrieb:

    Ich würde die Variablen immer erst da deklarieren wo ich sie brauche. Also im inneren der Schleife. Das macht den Code meiner Meinung nach besser zu lesen.

    Das würde ich gerne unterstützen! Vor allem fallen in diesem Fall dann auch die lästigen .clear() -Aufrufe weg.

    Wenn du optimieren willst, kannst du das getline + stringstream-Erzeugung vermeiden und die Zahlen direkt aus dem Dateistream lesen. Dann muss man nur aufpassen, selbst Whitespace zu überspringen und im Falle eines Umbruchs die Behandlung des Umbruchs selbst machen. Werner Salomon hätte dafür bestimmt auch eine Lösung in "schön" 😉



  • das war meine frage vor einigen Tagen in einem verwandten Thread als ich fragte ob man Objecte lieber in Loops immer wieder aufs Neue deklariert oder einmal vor der Loop und dafür dann wieder resettet.

    Da war der Tenor, dass man bei größeren Objekten lieber zweitere Lösung bevorzugt.

    Was wäre der Vorteil von nem eigenen Parser gegenüber nem stringstream?



  • Sewing schrieb:

    Da war der Tenor, dass man bei größeren Objekten lieber zweitere Lösung bevorzugt.

    Nein, war er nicht!



  • hab das ganze jetzt modifiziert gemäß euren Anregungen

    std::string line{}; 
      for (auto const &filename : FileNames_) {
        if (filename.find("Image") != std::string::npos) continue;
        std::ifstream inputFile{filename};
        assert(inputFile.is_open() && !inputFile.fail());
        for (size_t i = 1; i <= 11; ++i) {  // ignore the first 11 lines for pcd
          inputFile.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
        }
        while (getline(inputFile, line)) {
          if (line.empty()) continue;  // Ignore empty lines
          std::istringstream istream{line};
          pointClouds_[cloudName].emplace_back(
              std::istream_iterator<float>{istream},
              std::istream_iterator<float>{});
        }
      }
    


  • Sewing schrieb:

    das war meine frage vor einigen Tagen in einem verwandten Thread als ich fragte ob man Objecte lieber in Loops immer wieder aufs Neue deklariert oder einmal vor der Loop und dafür dann wieder resettet.

    Da war der Tenor, dass man bei größeren Objekten lieber zweitere Lösung bevorzugt.

    Dann hast du meinen Kommentar falsch verstanden.

    Sewing schrieb:

    Was wäre der Vorteil von nem eigenen Parser gegenüber nem stringstream?

    Dass er viel schneller sein wird. Das kann schnell mal Faktor 10 oder 100 ausmachen.
    Was nicht heisst dass es sich immer auszahlt. Ob 0.1ms oder 10ms (komplette Laufzeit des Programms) ist vermutlich Wurst. 1 Sekunde statt 1 Minute
    ist vermutlich nicht Wurst.

    Ist aber vermutlich ne interessante Übung falls du es noch nie gemacht hast.



  • ich danke dir



  • Mir ist gerade noch was anderes aufgefallen:

    assert(inputFile.is_open() && !inputFile.fail());
    

    Ein Assert ist eigentlich etwas, das immer korrekt sein soll und ansonsten einen Programmierfehler zeigen soll und z.B. in Release-Builds wegfallen kann. Finde ich "kreativ", das für den Erfolg von Dateioperationen zu verwenden 😉

    Du hast auch im weiteren Programmverlauf nur ungenügend Fehlerchecking. Was ist, wenn irgendeine Zeile irgendwo einen Nicht-Float enthält? Das fällt gar nicht auf, dann fehlen einfach die restlichen Einträge der entsprechenden Zeile.



  • Danke für die Anregungen, aber das open soll doch immer korrekt sein...
    wie hättest dus gemacht? Mit ifs und exceptions?

    was das parsen angeht:

    Ich bin da immer unentschlossen: Man kann doch nicht jedwede Möglichkeit abfangen...



  • Was ist denn, wenn die Datei z.B. nicht exisitiert? Dann schlägt die Assertion fehl.
    Üblicherweise willst du dein Programm dann nicht direkt beenden, sondern dem Nutzer zum Beispiel eine Meldung ausgeben, dass die Datei nicht existiert und der User den korrekten Dateinamen eingeben oder auswählen soll.

    Exceptions sind dafür zum Beispiel eine Möglichkeit.

    Das gilt auch fürs Parsen. Wie sieht es denn aus, wenn ein Programm abstürzt, weil eine fehlerhafte Datei eingelesen wird. Auch hier muss eine entsprechende Fehlermeldung an den Nutzer ausgegeben werden. Wenn was unvorhersgehenes geparst wird, macht dein Programm nachher irgendwas anderes, was möglicherweise in der nachfolgenden Verarbeitung zu Problemen führt. Und dann kommt man nicht mehr so leicht darauf, dass da der Fehler ja auch beim parsen liegen könnte.

    Da du deinen vector irgendwas mit Point Cloud genannt hast, je nach Sensor kommen da um Beispiel NaN oder infinity vor, da hab ich selbst blöde Erfahrungen mit machen müssen.


  • Mod

    Schlangenmensch schrieb:

    Was ist denn, wenn die Datei z.B. nicht exisitiert? Dann schlägt die Assertion fehl.
    Üblicherweise willst du dein Programm dann nicht direkt beenden, sondern dem Nutzer zum Beispiel eine Meldung ausgeben, dass die Datei nicht existiert und der User den korrekten Dateinamen eingeben oder auswählen soll.

    Interessanter ist doch die Frage, was passiert, wenn man im Release die Assertions ausschaltet. Hier wurde ein Codeprüfwerkzeug missbraucht, um Programmlogik umzusetzen.


Log in to reply