FSeek



  • Hallo Zusammen.

    Ich kämpfe seit Tagen mit einem kleinen Problem. Ich möchte Rohdaten (Datei) zur Aufbereitung in eine Textdatei umwandeln. Meine Dateien enthalten praktisch endlose Datenreihen die nacheinander angefügt werden. Der Aufbau jeder Reihe ist immer gleich, die Länge aber variabel: 4Byte stellen die Zeilennummer dar gefolgt von 4Byte die die anschließende Datensatzlänge angibt.

    Als Beispiel:
    820100007000000007000000A133A8DC4D64FE... für Zeile 386 mit einer Datensatzlänge von 112 Zeichen (nach den ersten 8 Byte die für die Beschreibung genutzt werden, also 120 wäre hier die gesamte Datensatzlänge).

    Das Ende der Datei wird immer mit FFFFFFFFFFFFFFFF markiert und es können durchaus hunderte von Datensätzen enthalten sein. Die Anzahl der enthaltenen Zeilen ist nicht bekannt und die Datensatzlängen können stark variieren. Erstellen möchte ich eine einfache Textdatei die nach dem obrigen Beispiel etwa so aussehen würde:
    386;112;7;A1;"RESTLICHE-DATEN-ALS-HEX"

    In der Schule hatte ich vor 25 Jahren mal ein bisschen Programmierung und dachte ich bekomm das hin. Ich hab das mal mit immer gleichen Datensatzlängen gemacht und das ging. Hier hab mir bisher aber echt einen abgebrochen, natürlich erfolglos. Ich dachte ich kann einfach eine Struktur vorgeben, die Datensatzlänge als INT einlesen und dann die Datensatzlänge über FSEEK bzw. SEEK_CUR benutzen und die Zeilen wie oben angegeben über einen Buffer in die Textdatei schreiben. Hat jemand eine Tipp für einen Oldie? Das sollte nach meiner Vorstellung ein einfacher Dreizeiler sein, schon peinlich dass ich das nicht mal selber hinbekomme... 😞

    Ich hab mal eine kleine Beispieldatei hinterlegt (667kb):
    http://rapidshare.com/files/409436936/SOT.dat
    MD5: ED23F8703C7A65ECA649DF22A0F7DCCB



  • Wozu hin- und hersuchen? Das ganze lässt sich doch wunderbar als Stream behandeln. Etwa

    #include <fstream>
    #include <iomanip>
    #include <vector>
    
    #ifdef _MSC_VER
    typedef unsigned __int32 uint32;
    #else
    #include <tr1/cstdint>
    typedef std::tr1::uint32_t uint32;
    #endif
    
    int main() {
      uint32 const marker = 0xffffffff;
    
      std::ifstream in ("SOT.dat");
      std::ofstream out("SOT.ssv");
    
      uint32 line;
      uint32 len;
      std::vector<unsigned char> data;
    
      while(in.read(reinterpret_cast<char*>(&line), 4) && 
            in.read(reinterpret_cast<char*>(&len) , 4) &&
            (len != marker || line != marker)) {
        data.resize(len);
    
        in.read(reinterpret_cast<char*>(&data[0]), len);
    
        out << std::dec
            << line                                 << ';'
            << len                                  << ';'
            << *reinterpret_cast<uint32*>(&data[0]) << ';'
            << std::hex
            << std::setfill('0')
            << std::setw(2)
            << static_cast<unsigned>(data[5])       << ";\"";
    
        for(std::size_t i = 6; i < data.size(); ++i) {
          out << std::hex
              << std::setfill('0')
              << std::setw(2)
              << static_cast<unsigned>(data[i]);
        }
    
        out << '"' << std::endl;
      }
    }
    

    Das ist in der Form natürlich nicht portabel; falls mit Little-Endian kodierte Dateien auf Big-Endian-Architekturen konvertiert werden sollen, musst du line, len und die ersten vier Byte in data vorher nach Big-Endian umwandeln (im Zweifel mit le32toh aus <endian.h>). Das ist generell ein Problem bei der Verarbeitung von Binärdaten.



  • Whow. Vielen Dank. Das ist so ziemlich komplett anders als meine ursprüngliche Idee wie man das umsetzen könnte, und vor Allem viel kürzer. 🤡
    Ich schaue mal ob ich damit auch klar komme. Hab gerade mal schnell versucht damit zu arbeiten hänge dann aber gleich beim Erstellen des Projektes mit 2 Fehlern:

    Fehler 1 error LNK2019: Verweis auf nicht aufgelöstes externes Symbol "_WinMain@16" in Funktion "___tmainCRTStartup". MSVCRTD.lib

    Fehler 2 fatal error LNK1120: 1 nicht aufgelöste externe Verweise. c:\Users\S\CPlusPlus\Seldon\Debug\De.exe 1



  • Du solltest eine Konsolenanwendung als Projekt benutzen...



  • Oder

    #include <windows.h>
    

    hinzufügen und

    int main()
    

    zu

    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
    

    ändern.



  • Th69 schrieb:

    Du solltest eine Konsolenanwendung als Projekt benutzen...

    Ja, mein Fehler, hab ich auch gerade gesehen.
    Konnte es mit den richtigen Einstellungen nun gut kompilieren. Leider liest das Programm nur die erste Zeile und das leider auch nicht vollständig bzw. fehlerhaft.

    Originaldatei:

    B10A0000 25010000 07000000 90 10 3C 38 91 85 86 EE 3D 2D BC 70
    22 EB 7C DD 2A 81 D1 87 13 59 13 97 28 8C 5A B5 7B BC C1 40 4C 5E 5F 37 FF FA
    1F 8D BD 66 08 3E F0 8B 5A 63 FD E6 0B 48 53 4E 1F 4E 64 61 8D 39 42 14 40 BB
    1F 39 7C 3C C4 93 72 A6 14 0B 96 57 F9 AE 25 21 24 0D 97 3E 9C C8 5C 19 67 54
    0F C6 CC 27 6A E4 B9 69 03 72 FB 7A E3 17 2C 07 FF 0D 5C 90 A8 E8 C3 89 4C 00
    F8 46 99 FC A2 E2 60 62 2C 05 FE 51 1E 95 98 F6 7B 95 E9 17 80 80 90 C3 2C D0
    87 13 99 38 29 8B 8A BE B7 11 CA DD 23 56 46 9C F2 51 2D 44 09 9A 36 F0 F5 46
    DA C1 D1 87 13 D9 4F D2 12 F4 E1 44 26 57 44 54 58 6F E2 51 19 6F E3 CF 61 97
    8B 1E F7 9A 54 60 DF C0 9C 46 C6 A4 0F 27 32 4B 4D 45 2D 7B 5D 71 CC 47 41 90
    DC 46 68 B8 09 52 01 D0 58 11 8F BE D3 A8 F9 63 A4 14 D5 74 B4 91 30 07 86 B0
    76 DC 58 57 33 26 E1 2B 55 54 BF E9 79 C8 0F 2B AA 1C 6A 48 04 FE 05 1A BE 27
    AA A8 15 83 5D F3 31 93 0A 35 5A 69 2C 72 2C 56 3F

    Ausgabe:

    2737;293;7;10;"3c38918586ee3d2dbc7022eb7cdd2a81d18713591397288c5ab57bbcc1404c5e5f37fffa
    1f8dbd66083ef08b5a63fde60b48534e1f4e64618d39421440bb
    1f397c3cc49372a6140b9657f9ae2521240d973e9cc85c196754
    0fc6cc276ae4b9690372fb7ae3172c07ff0d5c90a8e8c3894c00
    f84699fca2e260622c05fe511e9598f67b95e917808090c32cd0
    87139938298b8abeb711cadd2356469cf2512d44099a36f0f546
    dac1d18713d94fd212f4e14426574454586fe251196fe3cf6197
    8b1ef79a5460dfc09c46c6a40f27324b4d452d7b5d71cc474190
    dc4668b8095201d058118fbed3a8f963a414d574b491300786b0
    76dc58573326e12b5554bfe979c80f2baa1c6a4804fe05000000
    0000000000000000000000000000000000
    "

    @Nukularfüsiker

    Danke! Das werd ich auch mal ausprobieren als WIN-Anwendung. Ich hab noch nie eine gemacht, wollte das sowieso mal ausprobieren so mit Fenster usw..



  • So, ich hab das Script ein wenig geändert:

    //Script by seldon
    //http://www.c-plusplus.net/forum/
    
    #include <fstream> 
    #include <iomanip> 
    #include <vector> 
    
    #ifdef _MSC_VER 
    typedef unsigned __int32 uint32; 
    #else 
    #include <tr1/cstdint> 
    typedef std::tr1::uint32_t uint32; 
    #endif 
    
    int main() { 
      uint32 const marker = 0xffffffff; 
    
      std::ifstream in ("SOT.dat"); 
      std::ofstream out("SOT.txt"); 
    
      uint32 line; 
      uint32 len; 
      std::vector<unsigned char> data; 
    
      while(in.read(reinterpret_cast<char*>(&line), 4) && 
            in.read(reinterpret_cast<char*>(&len), 4) && 
            (len != marker || line != marker)) 
      { 
        data.resize(len); 
    
        in.read(reinterpret_cast<char*>(&data[0]), len); 
    
        out << std::dec 
            << line                                 << ';' 
            << len                                  << ';' 
            << *reinterpret_cast<uint32*>(&data[0]) << ';' 
            << std::hex 
            << std::setfill('0') 
            << std::setw(2) 
            << static_cast<unsigned>(data[4])       << ";"; 
    
    //  for(std::size_t i = 5; i < data.size(); ++i) Datenbeginn ohne 4 Byte-Nummerierung und 1 Byte Typenkennziffer
    	for(std::size_t i = 0; i < data.size(); ++i) 
    	{ 
          out << std::hex 
              << std::setfill('0') 
              << std::setw(2) 
              << static_cast<unsigned>(data[i]); 
        } 
    
        out << ';' << std::endl; 
      } 
    }
    

    Mir ist zwar noch nicht klar warum es die letzten 20 Byte mit 0 auffüllt. Ändere ich die Länge des Datensatzes in der Datei um -20 (ohne die Angabe der Zeilenlänge zu ändern) crashed der Vorgang beim Ausführen (vermutlich weil der nächste Datensatz nicht richtig gelesen werden kann), eine TXT-Datei entsteht aber trotzdem. Hier werden die letzten 20 Zeichen erstaunlicherweise nicht mit 0 aufgefüllt, sondern der Beginn der nächsten Zeile angehängt. Versteh ich momentan absolut nicht. Probier ich die Tage noch mal, auch das weitere Einlesen der restlichen Zeilen bis zum FFFFFFFF am Ende. Danke nochmals für die Hilfe und den kompletten Ansatz!!! Sieht viel besser aus als meine ursprüngliche Idee die nie funktionierte! 👍



  • Probier's mal mit

    std::ifstream in ("SOT.dat", std::ios::binary);
    

    Das hab ich Vollhorst übersehen (macht unter Linux keinen Unterschied).



  • seldon schrieb:

    Probier's mal mit

    std::ifstream in ("SOT.dat", std::ios::binary);
    

    Das hab ich Vollhorst übersehen (macht unter Linux keinen Unterschied).

    Das war's!!!!

    Ich hatte auch noch ein wenig rumprobiert und ohne "std::ios::binary" stoplert das Programm über jeden Wert mit "1A". Wenn ich alle 1A ersetze (z.B. mit 0A) klappte es auch mit der alten version. So ist es aber perfekt.

    Ich danke Dir! 🙂



  • Es dürfte auch einige andere Bytefolgen geben, über die er stolpert - \r\n (0x0d 0x0a) wird von Windows im Textmodus beispielsweise nach \n übersetzt. Sobald Steuerzeichen auftauchen, kann es da Probleme geben.

    Das war halt ein Flüchtigkeitsfehler, genau so wie der off-by-one-Fehler. Das hab ich davon, wenn ich so was in einer Viertelstunde ohne Tests zusammenschustere :(.

    Und, wie gesagt, wenn das ganze portabel sein soll, musst du die Endian-Problematik entsprechend deiner Umgebung lösen. Ich vermute aber, dass das nicht der Fall ist, denn in solchen Szenarien speichert man Zahlen üblicherweise in Netzwerk-Byteordnung (also Big-Endian). Trotzdem würde ich an geeigneter Stelle einen Kommentar darüber in den Code schreiben, denn niemand weiß, was die Zukunft bringt.


Anmelden zum Antworten