C++ Einstieg - Dateien einlesen



  • Ok, damit habe ich schon begonnen. Mir ging es eigentlich mehr um einige Besonderheiten diese FIles und wie ich damit am besten arbeite:

    Die Datei besteht aus Blöcken die durch öffnende und schließende Klammerpaare begrenzt sind. Die erste Zahl nach dem Öffnen des Blocks gibt den Typ an und legt damit fest, ob ich den Block brauche oder nicht. Jetzt wäre es vielleicht sinnvoll, die Datei schon beim einlesen in Blöcke zu zerlegen und nur di eBlöcke zu behalten, die ich brauche, oder? Wie könnte ich das angehen?

    Danke BastiL



  • BastiL schrieb:

    Jetzt wäre es vielleicht sinnvoll, die Datei schon beim einlesen in Blöcke zu zerlegen und nur di eBlöcke zu behalten, die ich brauche, oder? Wie könnte ich das angehen?

    Das hängt auch davon ab, wie du einliest. Wenn du mit std::fstream einliest und den Text in einem std::string (genauer gesagt in einem Container davon) speicherst, kannst du je nach Fall eben den Container um einen neuen Eintrag erweitern oder nicht.

    Falls dir nicht klar ist, was Container sind, kannst du mal hier schauen.



  • Nexus schrieb:

    und den Text in einem std::string (genauer gesagt in einem Container davon) speicherst, kannst du je nach Fall eben den Container um einen neuen Eintrag erweitern oder nicht.

    Ok das werde ich mir ansehen, danke. Kann ich in dem Container auch binäre Daten speichern oder müssten die dann direkt beim Einlesen vor dem speichern interpretiert werden?

    BastiL



  • Du kannst auch binäre Daten, zum Beispiel als char in einem std::vector<char> speichern - also Byte für Byte.



  • So ich habe mal angefangen. Ich lese den String jetzt so - so wie ich das verstehe ist das dann kein Container?

    std::ifstream infile("Text.txt", std::ios::binary);  
          std::ostringstream os;
        os << infile.rdbuf(); 
        std::string parse_str(os.str());
    

    Habe ich so noch Zugriff auf die Binärdaten oder ist das "kaputt"?

    Außerdem hat sich gezeit, dass meine Idee mit dem LEvel zählen schief geht - die BInärdaten verwirren den Zähler... Deshalb suche ich nach Alternativen. Es dürfe nicht ganz so einfach werden den Stream direkt beim Einlesen aufzuspalten. Daher brauche ich wohl reguläre Ausdrücke um hinterher die Header zu suchen, was meint ihr?

    Danke.



  • BastiL schrieb:

    Die Datei besteht aus Blöcken die durch öffnende und schließende Klammerpaare begrenzt sind. Die erste Zahl nach dem Öffnen des Blocks gibt den Typ an und legt damit fest, ob ich den Block brauche oder nicht. Jetzt wäre es vielleicht sinnvoll, die Datei schon beim einlesen in Blöcke zu zerlegen und nur di eBlöcke zu behalten, die ich brauche, oder?

    Ich würde die Datei einfach von vorne nach hinten durchgehen und die Struktur aufbauen, dabei die unnützen Blöcke weglassen, klar. Dabei solltest du dir aber erst überlegen, wie du den Inhalt der Datei in deinem Programm repräsentieren willst. Auf den ersten Blick sieht's nach sowas aus:

    struct Block
    {
        enum Type
        {
            Bla = 0,
            Blubb = 4,
            Foo = 37,
            ...
        };
    
        std::string        content; // Oder was komplexeres/zusammengesetztes
        std::vector<Block> sub_blocks;
    };
    
    std::vector<Block> data;
    

    Wenn du die Datenstruktur hast, kannst du dich an's Einlesen machen, vorher macht's imho keinen Sinn. Der Level-Zähler kann sinnvoll sein, muss aber nicht. Da die Blöcke anscheinend verschachtelt sind, wirst du wohl eh rekursiv parsen müssen, da ist der Level egal.
    Mit boost::spirit oder Regex-Krams würde ich das Parsen nicht angehen, da braucht die 5GB-Datei wahrscheinlich 'ne Woche bis sie geladen ist, abgesehen von dem Binär-Krams dazwischen. Und so kompliziert scheint das Format nicht zu sein.

    Für den Anfang: Rohe Bytes einzulesen geht etwa so:

    std::ifstream infile("Text.txt", std::ios::binary);  
    infile.seekg( 0, std::ios::end );
    size_t filesize = infile.tellg();
    infile.seekg( 0, std::ios::beg );
    
    std::vector<char> buffer( filesize );
    if ( filesize > 0 )
        infile.read( &buffer[0], filesize );
    


  • Danke Badestrand

    was Du mit rekursiv parsen meinst verstehe ich aber nicht. Außerdem ist es knifflig gleich beim Einlesen die Struktur aufzubauen weil das Ende der Blöcke schwer zu finden ist - ein einfaches Suchen nach ')' scheidet aus. Aber wahrscheinlich hast Du viel bessere Ideen als ich?



  • Das war genau das, was er gemeint hat mit rekursiv. 😉

    Das heisst du gehst "einfach" durch den Text und sobald du zu einem ( kommst, weisst du, dass jetzt ein Block folgt, also kannst du die Funktion, die dir einen Block liest nochmal aufrufen (rekursiv halt) bist irgendwann das erste ) kommt, dann geht es wieder zurück.

    Das kannst du dir so vorstellen, wie, wenn du deine UrUrUr Grosseltern frägst, wie denn alle ihre abkömmlinge heissen. (Zuerst werden die Kinder aufgezählt, dann die Kinder der Kinder usw. ) Im Code sieht das dann etwa so aus:

    kinder[] kind::gibKinder ( kind )
    {
       kinder[] = NULL;
    
       foreach (kinder as k )
         kinder[] = k->gibKinder ();
    
       return kinder;
    }
    


  • Ok...

    so etwas ähnliches habe ich mit meinem LEvel-Zähler versucht und es lief nicht, weil die Binärzeichen teils als öffnende oder schließende Klammern interpretiert werden.....



  • BastiL schrieb:

    es lief nicht, weil die Binärzeichen teils als öffnende oder schließende Klammern interpretiert werden.....

    Das Format wird ja irgendwie spezifizieren, wie der Binärstrom anfängt und endet. Den dann also gesondert parsen 🙂



  • Badestrand schrieb:

    Das Format wird ja irgendwie spezifizieren, wie der Binärstrom anfängt und endet. Den dann also gesondert parsen 🙂

    Nicht wirklich. Das wird ebenfalls durch öffnende bzw. schließende Klammern gemacht. Allenfalls durch den jeweiligen Header.


Anmelden zum Antworten