binärdaten und Text lesen



  • folgendes Problem:

    eine Datei besteht teilweise aus Text, teilweise aus Binärdaten (Bytes). ich will die Datei einlesen, und zwar so, daß die Textpassagen in einem vector<string> landen, die Binärdaten byteweise in Dezimalschreibweise umgewandelt werden und (durch Blanks getrennt) als String zurückgegeben werden.

    wie man das machen kann, ist mir schon klar, und habe ich auch schon gemacht, kommt mir aber wenig elegant vor, außerdem komme ich dabei nicht ganz ohne C-Funktionen aus.

    Wie sieht ein idiomatischer C++ Weg für so etwas aus ?


  • Mod

    Gib mal genauer das Format an.



  • das genaue Format spielt keine Rolle, es geht mir mehr ums Prinzip, gemischt binär-Text-Dateien in C++ elegant zu verarbeiten.

    nimm im einfachen Fall mal an, ein paar Zeilen mit Dezimalzahlen (durch whitespace getrennt), gefolgt von Binärbytes, wobei die Zeilen mit Dezimalzahlen durch Kommentarzeilen unterbrochen sein dürfen. Der Rest Binärbytes. Also etwa

    // blabla
    1 2 3 4 5 
    // bla
    // bla
    // bla 
    6 7 8 9
    //bla
    (_'-'!%$ "%&$ &(9&)(8&/7%)6&)&5("!%&%&"&
    %&%$%!"§$%$)))"%"!$%§&%((!"4/86%§("&!)$
    !()&"$8!(/")=&$=&=/(!$&§/&=("$&!($&(!$&((&
    


  • Edit: Woran erkennst du, dass der "String-Teil" der Datei beendet ist?


  • Mod

    Wie sollen diese geheimnisvollen "Binärbytes" gelesen werden? Einfach in einen string?
    Außerdem: Sag mal mehr zum Format. Irgendwas mit Zeilen und Kommentaren und Zahlen ist da ja anscheinend noch. Das ist doch schon mal sehr viel Format, dass du gar nicht erklärst.



  • SeppJ schrieb:

    Wie sollen diese geheimnisvollen "Binärbytes" gelesen werden?

    das will ich ja von DIR wissen, sonst hätte ich nicht gefragt 😃

    Einfach in einen string?

    Tja. Schlecht, weil im Binärteil \0 vorkommen kann und auch haufenweise vorkommt.

    mein momentaner Ansatz ist, das ganze File erstmal in ein char* einzulesen und dieses dann Byte-weise zu parsen. Dabei kann ich wahlweise C-Stil (sscanf) oder C++ (istringstream) benutzen.

    Ich habe davor andere Ansätze probiert, bspw zeilenweise einlesen in einen vector<string> mit getline(...) und die Zeilen zu parsen, aber das hat sich nicht bewährt, weil ich herausgefunden habe, daß in solchen Dateien manchmal Zeilenenden durch Leerzeichen ersetzt sind, sodaß ich nach whitespace parsen muß, da fällt getline meines Wissens schon mal weg.

    Meine Frage zielt aber darauf ab, ob es eine elegante, kanonische Lösung in C++ gibt. Byteweise zu parsen kommt mir nicht sonderlich elegant vor, funktioniert aber immerhin.



  • Jodocus schrieb:

    Edit: Woran erkennst du, dass der "String-Teil" der Datei beendet ist?

    im Beispiel würde das daran erkannt, daß 1 2 3 4 5 und 6 7 8 9 (eine feste Anzahl Dezimalzahlen) gelesen worden sind, ansonsten nur Kommentarzeilen waren. Dann beginnt mit der nächsten Nicht-Kommentar-Zeile der Binärteil. Der natürlich nicht mehr in Zeilen strukturiert ist, weil dort die Rohdaten 0x0a 0x0d, 0x00 usw vorkommen können und es auch tun.



  • großbuchstaben schrieb:

    Meine Frage zielt aber darauf ab, ob es eine elegante, kanonische Lösung in C++ gibt. Byteweise zu parsen kommt mir nicht sonderlich elegant vor, funktioniert aber immerhin.

    Mir ist überhaupt keine kanonische Lösung bekannt. Weder mit C noch mit C++.

    Problem ist das Newline. Wenn du im Textmode öffnest, ist auf Windows \r\n = \n. Das ist für Binärdaten schlecht, also kann man den Textmodus vergessen.

    Andererseits ist im Binärmodus \r\n = \r\n, d.h. wenn du eine Zeile einliest, hast du ein störendes \r am Ende des strings.

    Weil die C++ Iostreams stark Wert auf Separation Of Concerns gelegt haben, ist es mit Streambuffern ganz intuitiv möglich, Binärdaten einzulesen:

    std::string die_daten;
    die_daten.resize(so_viel_wie_ich_will);
    file.rdbuf()->sgetn(&die_daten[0], so_viel_wie_ich_will);
    


  • kanone schrieb:

    Mir ist überhaupt keine kanonische Lösung bekannt. Weder mit C noch mit C++.

    Klar, es ist wohl auch kein kanonisches Problem.



  • großbuchstaben schrieb:

    Einfach in einen string?

    Tja. Schlecht, weil im Binärteil \0 vorkommen kann und auch haufenweise vorkommt.

    std::string hat kein Problem mit '\0', im Gegensatz zu char *. Du kannst also alles was Text ist normal mit dem String bearbeiten und den Binärteil halt byteweise auch mit std::string.

    großbuchstaben schrieb:

    Jodocus schrieb:

    Edit: Woran erkennst du, dass der "String-Teil" der Datei beendet ist?

    im Beispiel würde das daran erkannt, daß 1 2 3 4 5 und 6 7 8 9 (eine feste Anzahl Dezimalzahlen) gelesen worden sind, ansonsten nur Kommentarzeilen waren. Dann beginnt mit der nächsten Nicht-Kommentar-Zeile der Binärteil. Der natürlich nicht mehr in Zeilen strukturiert ist, weil dort die Rohdaten 0x0a 0x0d, 0x00 usw vorkommen können und es auch tun.

    Können auch die Rohdaten 3 2 1 \r \n vorkommen? Wie weißt du dann dass es Rohdaten sind und keine Zeile? Wahrscheinlich bedeutet die Zahl wie lang die Binärdaten sind.

    Du kannst leicht das erste nicht-ASCII-Zeichen finden und dort den String splitten. Oder das erste nicht-ASCII-Zeichen und dann das letzte \r\n davor und dort splitten. Dann hast du beide Teile und kannst sie vernünftig weiter verarbeiten. Das "splitten" macht man natürlich nur logisch, nicht indem man tatsächlich zwei strings anlegt. Ich habe jedenfalls noch nicht erkannt, warum std::string für deine Zwecke nicht reicht.



  • nwp3 schrieb:

    Können auch die Rohdaten 3 2 1 \r \n vorkommen? Wie weißt du dann dass es Rohdaten sind und keine Zeile?

    ja, die Rohdaten sind [0...255]* ohne Einschränkung. Den Beginn der Rohdaten erkenne ich daran, daß 1) die vorher vereinbarte Anzahl an Dezimalzahlen eingelesen worden ist, und 2) auf das Zeilenende der letzten eingelesenen Kommentarzeile kein '//' folgt. Dann kann die nächste Zeile keine Textzeile mit Zahlen sein, und auch keine Kommentarzeile, muß also Beginn der Binär-Passage sein.



  • nwp3 schrieb:

    Du kannst leicht das erste nicht-ASCII-Zeichen finden und dort den String splitten.

    leider nicht, denn die Rohdaten könnten aussehen wie 0x32 0x40 0x41 0x42 0x35 0x50, also nur aus druckbaren Zeichen bestehen. Es gibt keine Einschränkung.


Anmelden zum Antworten