Eine bestimmte Zeile aus einer großen .txt auslesen



  • Touring mit nem endlichen Band?



  • padreigh schrieb:

    Touring mit nem endlichen Band?

    Warum müssen wir raten? Weil du uns nicht sagst um was es geht.

    Ich rate auch mal und sage, dass man solche Datenmassen am besten mit einer Datenbank löst und nicht mit Textdateien. Das ist wenigstens vernünftig.



  • Warum kannst du in dem Fall nicht einfach die ganze Datei einlesen und dann basierend darauf dein Programm ausführen? Überleg doch mal was für einen wahnsinnigen Overhead du sonst für wohl einer der wesentlichsten Funktionen überhaupt, nämlich einem simplen Jump Befehl, hast. Vor allem sind Jumps an den Anfang der Datei dann wesentlich schneller als Jumps an Stellen weiter hinten. Wenn du mich fragst ist das ziemlicher Mist...



  • Also wenn die Datei so aussieht, solltest Du den Inhalt auf einmal im Speicher halten. Aber bitte nicht als strings, denn Du brauchst keine strings, sondern als eine Struktur, die Deinen Anforderungen angemessen ist. Beim Einlesen einer 'Zeile' solltest Du gleich so eine Struktur füllen.

    Anbei ein Code-Schnipsel was in die Richtung geht, die ich meine:

    #include <algorithm> // find_if
    #include <fstream>
    #include <iostream>
    #include <string> //string, getline
    #include <vector>
    
    struct Cmd
    {
        int m_label;
        std::string m_cmdType; // Bem.: ggf. durch enum ersetzen, braucht auch weniger Platz & ist schneller
        std::string m_param; // Bem.: Optinmierungsbedarf (groß), aber wie sind die genaue Anforderungen?
    };
    // --  liest ein Cmd-Objekt vom istream
    std::istream& operator>>( std::istream& in, Cmd& cmd )
    {   // Format: <Label> <Kommanodwort> <Parameter>      // Bem.: Parameter darf nicht leer sein
        return std::getline( in >> cmd.m_label >> cmd.m_cmdType >> std::ws, cmd.m_param );
    }
    
    struct Line // Funktor für die Suche nach den Zeilen/Labels
    {
        Line( int lable=0 ) : m_label( lable ) {}
        bool operator()( const Cmd& cmd ) const
        {
            return cmd.m_label == m_label;
        }
    private:
        int m_label;
    };
    
    int main()
    {
        using namespace std;
        vector< Cmd > cmds;
        ifstream datei("input.txt");
        datei >> hex; // die Labels sind als HEX hinterlegt
        for( Cmd cmd; datei >> cmd; )
            cmds.push_back( cmd );
    
        // --   usw.
        //      z.B. eine bestimmte 'Zeile' suchen
        vector< Cmd >::iterator i = find_if( cmds.begin(), cmds.end(), Line( 0x3CA ) );
        if( i != cmds.end() )
        {
            cout << "naechstes Kommando ist: " << i->m_cmdType << endl;
        }
        return 0;
    }
    

    Gruß
    Werner



  • Hingeklatscht und faul ... einfach alles einlesen. Datei besteht aus 12001 identischen Zeilen "12345678 90 12345678 90 12345678 90 12345678 9" .. das braucht 6ms zum einlesen im Debugcompilat ... also kaum ein "Hinderungsgrund" es nicht zu tun. Stundenlang warten da höchsten Trolle.

    #include <iostream>
    #include <fstream>
    #include <vector>
    #include <string>
    #include <QTime>
    int main()
    {
        using namespace std;
    
        QTime time;
        time.start();
        vector<string> alles;
        alles.reserve(13000);
        {
            ifstream datei("test.txt");
            string line;
    
            while( datei.good() )
            {          
                getline(datei,line,'\n');
                alles.push_back(line);
                // std::cout << alles.size() << std::endl;
            }
        } // wer braucht schon exceptions ;)
    
        std::cout << time.elapsed();
    }
    


  • dot schrieb:

    . Vor allem sind Jumps an den Anfang der Datei dann wesentlich schneller als Jumps an Stellen weiter hinten.

    Ja, das hab ich mir auch schon überlegt. Ganze Datei einlesen, dann funktioniert es super, die Zeit/Speicher habe ich jetzt auch ausprobiert und das ist auch Ok.

    Danke für eure Hilfe



  • Wenn das ähnlich zu Assembler-Programmcode wird, dann gibt es aber nur sehr wenige Sprungziele. Und wenn nicht mit goto gesprungen wird, gehts sequenziell weiter zur Folgezeile. Dann müßte man fseek nur machen, wenn ein Sprung stattfindet. Bei 10000 Zeilen Code vielleicht nur 500 Sprungziele. Dann kannste auch nur die cachen.



  • Ja, ich werde nicht so viele Sprungbefehle brauchen, aber wichtig ist, das ich sie verwenden kann. Sonst lese ich normalerweise mit der nächsten Zeile weiter.



  • padreigh schrieb:

    ifstream datei("test.txt");
            string line;
    
            while( datei.good() )
            {          
                getline(datei,line,'\n');
                alles.push_back(line);
                // std::cout << alles.size() << std::endl;
            }
    

    Diese Konstruktion liest eine Zeile zu viel, wenn das letzte Zeichen der Datei ein '\n' ist. Was in der überschüssigen Zeile steht, ist nicht definiert und von der Implementierung von getline abhängig.

    Vor diesen while(good)-lese-verarbeite oder schlimmer while(!eof)-lese-verarbeite -Konstruktionen kann man nur warnen. Für das Einlesen gilt das, was bei jedem Funktionsaufruf gilt, der einen Fehler produzieren kann.
    ZUERST aufrufen(hier lesen) und DANACH prüfen, ob es gut gegangen ist - und nicht umgekehrt. Also wenn unbedingt mit while, dann

    ifstream datei("input.txt");
            string line;
            while( getline(datei,line,'\n') )
            {          
                alles.push_back(line);
                // std::cout << alles.size() << std::endl;
            }
    

    Gruß
    Werner



  • MGOS schrieb:

    Ja, ich werde nicht so viele Sprungbefehle brauchen, aber wichtig ist, das ich sie verwenden kann. Sonst lese ich normalerweise mit der nächsten Zeile weiter.

    Ich würde das in jedem Fall alles zunächst einlesen. Du musst schon sehr wenig Speicher in Deinem PC haben, bevor Du da ein Problem bekommst.
    Und wenn das so eine Art Interpreter werden soll, der ein Script abarbeitet, so würde ich mehrere Typen von Kommandos implementieren, die eine gemeinsame Basisklasse haben.

    Wie viele verschieden 'Codes' (wie GOTO) kommen denn vor?
    Ist das so eine Art 'Text Adventure'?
    hat jede Zeile ein Label/Nr vor dem 'Code' stehen?



  • Is wird ca. 80 verschiedene Befehl-Codes geben, von denen viele sehr ähnlich sind, deshalb werde ich diese auch splitten, damit es weniger Aufwand ist.

    Normalerweiße hat jeder Code eine Nummer davorstehen, aber da der Anwender die .txt bearbeiten darf und muss, können sich da Fehler einschleichen, die erst überprüft werden müssen.



  • dot schrieb:

    Wenn du Zeile 7861 willst, warum nicht einfach:

    std::ifstream file(filename);
      int n = 1;
      while (file && n < 7861)
        if (file.get() == '\n')
          ++n;
      std::string line;
      std::getline(file, line);
    

    Das dürfte aber die ineffizienteste Variante von allen sein, oder?
    Schließlich wird nur Zeichen für Zeichen gelesen und kein größerer Buffer verwendet.


  • Mod

    m,.m,. schrieb:

    Schließlich wird nur Zeichen für Zeichen gelesen und kein größerer Buffer verwendet.

    Eventuelle Buffer liegen viel tiefer als irgendwelche Einleseroutinen der Standardbibliothek.



  • SeppJ schrieb:

    m,.m,. schrieb:

    Schließlich wird nur Zeichen für Zeichen gelesen und kein größerer Buffer verwendet.

    Eventuelle Buffer liegen viel tiefer als irgendwelche Einleseroutinen der Standardbibliothek.

    Kann man das also auch in produktiven Code so realisieren? Zu meinen Lieblingsthema gehört die Netzwerkprogrammierung und hier ist es z.B. nicht sehr effizient, wenn man nur jeweils 1 Zeichen einliest. Deshalb wäre ich jetzt der Meinung gewesen, dass selbiges auch auf alle anderen IO-Operationen zutrifft.

    Ich gehe natürlich schon davon aus, dass zumindest std::ifstream selber puffert, aber das sind ja letztendlich Implementationsdetails auf die man genau genommen nicht vertrauen kann.


  • Mod

    m,.m,. schrieb:

    Kann man das also auch in produktiven Code so realisieren?

    Ehrlich gesagt: Nein. Habe es nämlich gerade ausprobiert und mich da wohl getäuscht. Zwischen istream::get und istream::read mit großen Blöcken liegen Welten.


Anmelden zum Antworten