Binärdaten sauber in Logdatei schreiben

Gast #5001804
Hallo zusammen,

ich habe eine ganze Menge Daten, welche ich (mit einem STM32) auf eine 
SD-Karte logge. Es ist eine Mischung aus 32Bit, 16Bit und 1Bit Daten. 
Aktuell logge ich alle Daten als lesbare Zeichen. Da die Logfiles 
dadurch recht groß werden, wäre es sinnvoller die Daten direkt binär zu 
schreiben (spart Speicherplatz und das Ausgeben/Einlesen der Dateien 
wird schneller).
Mein Problem: Wie trenne ich die einzelnen Datensätze voneinander? Wenn 
ich davon ausginge, dass es niemals einen Schreibfehler geben kann, dann 
könnte ich natürlich von der bekannten Datensatzgröße ausgehen. Aber 
wenn ich das nicht garantieren kann, dann muss ich irgendein (oder 
mehrere) Trennzeichen haben, welches aber niemals in den Daten selber 
vorkommen kann.

Gibt es hierfür eine sinnvolle Lösung, ausser die Daten daraufhin zu 
überprüfen, dass nicht zufällig das gewählte Trennzeichen darin 
vorkommt?

Danke und Gruß, Heiko
Gast
Heiko schrieb:
> Da die Logfiles
> dadurch recht groß werden, wäre es sinnvoller die Daten direkt binär zu
> schreiben (spart Speicherplatz und das Ausgeben/Einlesen der Dateien
> wird schneller).
Ist das derzeit oder auf absehbare Zeit tatsächlich ein Problem für 
deine Anwendung? Nichts ist ätzender, als Log-/Konfigurationsdateien, 
die nur mit Spezialwerkzeug zu lesen sind...

Heiko schrieb:
> Gibt es hierfür eine sinnvolle Lösung, ausser die Daten daraufhin zu
> überprüfen, dass nicht zufällig das gewählte Trennzeichen darin
> vorkommt?
Jein. Sich Gedanken über das Format der Datensätze machen...
Geh mal anders ran an das Problem: Stell dir vor, es wäre keine 
Logdatei, sondern eine serielle Schnittstelle. Da würdest du dir 
sicherlich auch irgendein Datensatzformat ("Protokoll") ausdenken, bei 
dem du jederzeit den Anfang eines Satzes erkennst. Vermutlich würdest du 
noch eine Prüfsumme dranhängen.

Du musst dir halt einen Satztrenner aussuchen und diesen maskieren, 
falls er in den Nutzdaten vorkommt.
Gast
Da Du leider nicht geschrieben hast, in welcher Reihenfolge die Daten 
anfallen, kann ich Dir nur schreiben, wie ich das üblicher Weise mache.

Ich definiere meine Logdaten als struct (in C) und schreibe die Daten in 
dieser Größe binär sizeof(struct ....) auf die Platte.
Dadurch ist, solange die struct statisch ist, gewährleistet, dass immer 
die Satzgröße gleich ist.
Damit braucht man sich keine Satztrenner einfallen lassen und hat auch 
sonst
weniger Aufwand.
Lesen geht dann ebenso über die feste Satzlänge.
Wichtig dabei ist, dass man für beide Programme (Schreiben und Lesen) 
identische Compileroptionen setzt. Sonst geht das schief.
Persönliche Seite
So ganz klar ist mir die Datenstruktur nicht, d.h. ob die verschieden 
breiten Wörter zusammengehören oder unabhängig voneinander geschrieben 
werden. Aber es gab in der alten Zeit sog. fixed length records. Ein 
Marker am Anfang bestimmt den Typ des Datensatzes und von da an ist die 
Wortbreite klar, bspw. könntest du die ASCII-Zeichen 1, 6 und 3 
verwenden, gefolgt von jeweils dem 1-, 16- bzw. 32-bit-Wort.

Wenn hingegen immer die drei Datentypen zusammengehören, wird es nicht 
besser als 7 Byte-Blöcken, da ich für das 1-bit-Wort auch ein Byte 
verwenden würde. So geht es immer in 7er Schritten voran. Wenn 
Speicherplatz keine Rolle spielt, würd' ich auf 8bit aufrunden und bspw. 
immer einen Zeilenumbruch oder etwas ähnlich praktisches (NUL…) als 
Trenner einsetzen.
Heiko schrieb:
> Hallo zusammen,
>
> ich habe eine ganze Menge Daten, welche ich (mit einem STM32) auf eine
> SD-Karte logge. Es ist eine Mischung aus 32Bit, 16Bit und 1Bit Daten.

Du solltest vielleicht erläutern, ob Deine Datenmischung immer im selben 
Verhältnis ensteht (was die Struct-Lösung oben begünstigen würde), oder 
in beliebigen Mischverhältnissen.
Heiko schrieb:
> Mein Problem: Wie trenne ich die einzelnen Datensätze voneinander? Wenn
> ich davon ausginge, dass es niemals einen Schreibfehler geben kann, dann
> könnte ich natürlich von der bekannten Datensatzgröße ausgehen. Aber
> wenn ich das nicht garantieren kann, dann muss ich irgendein (oder
> mehrere) Trennzeichen haben, welches aber niemals in den Daten selber
> vorkommen kann.
>
> Gibt es hierfür eine sinnvolle Lösung, ausser die Daten daraufhin zu
> überprüfen, dass nicht zufällig das gewählte Trennzeichen darin
> vorkommt?

Die typische Lösung ist sowas wie 4 Byte "Synchronisationsmarker" im 
Datenstrom zu platzieren, die am Anfang eines Datensatzes stehen.

Die Wahrscheinlichkeit, dass in deinen Daten zufällig genau diese 4 Byte 
vorkommen ist verschwindend gering und selbst wenn es passiert, 
wiederholt die Synchronisationslogik die Suche nach dem Marker einfach 
für das nächste Paket noch einmal, sodass du nur ein Paket verlierst.

Also Idee ist, jedes Paket hat einen Marker am Anfang, und dann wird 
einfach kontinuierlich gelesen außer der Marker passt irgendwo nicht; in 
dem Fall ignorierst du einfach alle Daten bis zum nächsten Marker. Das 
ist sehr robust.
Gast
Sven B. schrieb:
> Die Wahrscheinlichkeit, dass in deinen Daten zufällig genau diese 4 Byte
> vorkommen ist verschwindend gering und selbst wenn es passiert,
> wiederholt die Synchronisationslogik die Suche nach dem Marker einfach
> für das nächste Paket noch einmal, sodass du nur ein Paket verlierst.

Als erste Lösung gut, aber unsauber. Darum wird das "zufällige 
Verlieren" mit Byte-Stuffing (hier Long-Stuffing) auf 0 reduziert. Sagen 
wir die Magic-Marker-Number soll 128, dann definieren wir noch eine 
weitere (129) und ersetzten jedes "natürliche" Auftreten von 128 oder 
129 durch zwei Zeichen: 129, (Zeichen + 10).

Der Start-Marker ist damit weiterhin 128, und wenn 129 auftaucht, wird 
die 129 gelöscht und das nächste Zeichen -10 genommen.
Achim S. schrieb:
> Sven B. schrieb:
>> Die Wahrscheinlichkeit, dass in deinen Daten zufällig genau diese 4 Byte
>> vorkommen ist verschwindend gering und selbst wenn es passiert,
>> wiederholt die Synchronisationslogik die Suche nach dem Marker einfach
>> für das nächste Paket noch einmal, sodass du nur ein Paket verlierst.
>
> Als erste Lösung gut, aber unsauber. Darum wird das "zufällige
> Verlieren" mit Byte-Stuffing (hier Long-Stuffing) auf 0 reduziert. Sagen
> wir die Magic-Marker-Number soll 128, dann definieren wir noch eine
> weitere (129) und ersetzten jedes "natürliche" Auftreten von 128 oder
> 129 durch zwei Zeichen: 129, (Zeichen + 10).
>
> Der Start-Marker ist damit weiterhin 128, und wenn 129 auftaucht, wird
> die 129 gelöscht und das nächste Zeichen -10 genommen.

Hmja, ist nett, aber ist das wirklich nötig? Daten verlierst du eh nur 
dann wenn sowieso schon was fehlerhaft ist, und dann höchstens ein Paket 
mehr als beschädigt wurde ... mit einer typischerweise völlig 
verschwindenden Wahrscheinlichkeit bei 4 Byte Marker ...
Heiko schrieb:
> dann muss ich irgendein (oder
> mehrere) Trennzeichen haben, welches aber niemals in den Daten selber
> vorkommen kann.

(1) Mit Endezeichen (bspw. irgendein Byte). Kommt das Endezeichen in den 
Daten vor, wird es dort doppelt geschrieben. Records werden als 
Bytesequenzen betrachtet und können unterschiedliche Länge haben.

(2) Bei ASCII benutzt du nur einen Teil des Zeichensatzes für die 
"Zahlen". Einen anderen Teil für Markierungen. Definierte dir deinen 
eigenen Zeichensatz, der dichtere Daten erlaubt (z.B. BCD, BASE64, 
etc.).

(3) Führe eine Checksum ein, die an das Ende des Records angehangen 
wird. (Damit sollte man schnell synchronisieren können.)

> ich habe eine ganze Menge Daten, welche ich (mit einem STM32) auf eine
> SD-Karte logge.

In einem Thread hier im Forum wurde kürzlich die (un)Eignung von SD 
Karten für zuverlässiges Speichern diskutiert.

Antwort schreiben

Eine eigene Frage? Dieser Thread ist über 9 Jahre alt. Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.

Neuen Thread starten

Eine Lösung, Ergänzung oder Rückmeldung zu diesem Thread? Dann antworte hier.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.