Besteht die Gefahr, rohe Bytes in eine Datei zu schreiben? [geschlossen]

12

Ich arbeite an einem Problem in Programming Pearls - speziell an der Implementierung eines Programms, das eine Datei sortiert, die höchstens 10.000.000 Ganzzahlen enthält (Spalte 1, Problem 3). Da im Buch nicht angegeben ist, wie die Daten in der Datei gespeichert werden sollen, erwäge ich, die Ganzzahlen als unformatierte Bytes zu speichern (es gibt einige andere Einschränkungen, die unformatierte Bytes zu einer guten Option machen). Ich habe noch nie auf einem so niedrigen Niveau gearbeitet, also möchte ich wissen, ob es etwas Gefährliches gibt, auf das ich achten sollte. Muss ich mir zum Beispiel Sorgen machen, wenn ich versehentlich eine Dateiendesequenz verwende, wenn ich rohe Bytes in eine Datei schreibe?

Bearbeiten:

Mir ist jetzt klar, wie umfassend meine Frage war. Ich meinte wirklich Probleme der katastrophaleren Art, wie das versehentliche Überschreiben anderer Dateien auf der Festplatte. Entschuldigung, ich war ursprünglich nicht klarer.

Drake Sobania
quelle
6
Beachten Sie, dass Programming Pearls ein sehr altes Buch ist. Sie können problemlos die gesamten 10 ^ 7 Ganzzahlen auf einem modernen Desktop-Computer in den Speicher lesen, sortieren und erneut schreiben. Begrenzen Sie die Menge, die Sie zu einem beliebigen Zeitpunkt lesen, auf einen Bruchteil der Gesamtzahl, um den ursprünglichen Punkt dieses Kapitels zu erhalten. Erhöhen Sie die Dateigröße auf ca. 10 ^ 10 Ganzzahlen.
Caleb
3
Wenn ich das Wort "gefährlich" höre, denke ich an Dinge, die meinen PC explodieren lassen, meine Bankkonten löschen oder ähnliches. Und ich denke, es ist sehr wahrscheinlich anzunehmen, dass - solange Ihr Programm nicht zur Steuerung eines Airbus oder eines Kraftwerks verwendet wird - nichts wirklich "Gefährliches" passiert, wenn Sie ausprobieren, was Sie vorhaben.
Doc Brown
2
@delnan Vor Jahren, als der Mythos des EOF-Zeichens noch in Mode war, erinnere ich mich an Kopierschutzsysteme, die auf "Kopieren bis zum EOF-Zeichen" basierten, wie es viele damalige Kopierprogramme taten. Einige Programme fügten zusätzliche Daten ein, die sie nach dem EOF-Marker einer zugeordneten Textdatei, jedoch vor dem zugewiesenen Ende der Datei, überprüften . Das Kopierprogramm würde die zusätzlichen Daten nicht kopieren, um eine Neuinstallation zu validieren ... ahh ... Nostalgie.
Achtung? Wie in "Sprengt mein Computer, wenn ich das tue"? Nee.
28.

Antworten:

11

Die einzige Gefahr, der Sie begegnen werden, ist Little vs. Big Endianess (ob das höchst- oder niedrigstwertige Byte zuerst geschrieben wird). Wenn Sie sich jedoch in derselben Umgebung befinden, tritt kein Problem auf. Neben der allgemeinen Sicherstellung des Schreibens / Parsens von Roundtrip.

Das Dateisystem ist für die Verarbeitung beliebiger Bytefolgen ausgelegt.

Ratschenfreak
quelle
2
+1 für die letzte Zeile. Ich bin nicht sicher, ob das große / kleine Problem das einzige Problem ist - das OP könnte zum Beispiel verwirrt werden, wo die Grenzen zwischen ganzen Zahlen liegen. Aber trotzdem eine gute Antwort.
Caleb
27

Nein, tatsächlich funktionieren so viele Dateiformate. Häufige Beispiele für solche Binärdateien sind Bilder und Musik- / Audiodateien.

Befolgen Sie die folgenden Richtlinien, um die Integrität der Datei und der daraus gelesenen Daten zu gewährleisten:

  • Öffnen Sie die Datei (Lesen oder Schreiben) immer im selben Modus: Text oder Binär. Der Hauptunterschied besteht darin, dass sich der Textmodus um Zeilenumbrüche kümmert und die Zeilenumbrüche beim Lesen einer Datei möglicherweise "abbrechen" kann (abhängig von der verwendeten Bibliothek). Der Textmodus führt möglicherweise auch Unicode-Übersetzungen durch, die wahrscheinlich bei Nicht-Unicode-Daten nicht mehr funktionieren.
  • Achten Sie beim Lesen von Nicht-String-Daten darauf, dass Sie denselben Datentyp wie beim Schreiben verwenden. Wenn die ersten vier Bytes der Datei beispielsweise eine beschreibende Ganzzahl sind, lesen und schreiben Sie unbedingt mit einer Methode, die eine Ganzzahl verwendet / bereitstellt, um sicherzustellen, dass sie konsistent behandelt wird. Derselbe Datentyp kann auf verschiedenen Computern unterschiedlich groß sein, und das Mischen von Datentypen auf demselben Computer kann auch die Bedeutung der Daten ändern (z. B. das Interpretieren eines Bits in der Mitte einer längeren Ganzzahl als Vorzeichenbit).
  • Endianness: Wenn die Bibliothek, die Sie verwenden, dies nicht konsistent handhabt, müssen Sie dies möglicherweise selbst tun. Beispielsweise verwendet Java für Multi-Byte-Typen immer die Netzwerk-Byte-Reihenfolge (Big Endian). C und C ++ verwenden alles, was der Bibliotheksimplementierer beschlossen hat, normalerweise dasselbe wie der Prozessor (Little Endian bei Intel, Big Endian bei den meisten anderen). Wenn dies eine schnelle Übung für ein System ist, ist es nicht so wichtig, aber es ist immer noch eine gute Angewohnheit, darauf zu achten und sie bei Bedarf zu umgehen.

Die spezifischen Details variieren je nach Framework, Plattform und Sprache. Dies sollte jedoch die grundlegenden "Fallstricke" mit Datei-E / A abdecken.


quelle
3
Ein zusätzlicher Punkt für Nicht-String-Daten: Stellen Sie sicher, dass Sie für jeden Typ eine einheitliche Anzahl von Bytes verwenden. In C und C ++ intkann ein Wert irgendwo zwischen 2 und 8 oder mehr Bytes liegen (eigentlich Oktette).
Bart van Ingen Schenau
Das ist implizit in meinem zweiten Punkt enthalten, zB 32 v. 64 Bit Integer. Das wären unterschiedliche Datentypen.
Vielleicht möchten Sie es explizit machen. Es ist nicht offensichtlich, dass intauf zwei verschiedenen Maschinen unterschiedliche Datentypen berücksichtigt werden können.
Bart van Ingen Schenau
9

Wenn Sie nicht nur Daten in einem vorhandenen Format lesen und schreiben, sondern auch ein neues Binärdateiformat erstellen möchten, müssen Sie zusätzlich zu allen bereits erwähnten Fallbeispielen unbedingt einen Dateikopf einfügen : einen Datenblock am Anfang der Datei, die das Dateiformat eindeutig identifiziert und alle erforderlichen Metadaten aufzeichnet.

Gute Datei-Header enthalten mindestens drei Dinge:

  • Eine " magische Zahl " von mindestens vier Bytes. Die magische Zahl MUSS rfc2119 die allerersten N Bytes in der Datei sein, MUSS niemals für ein anderes Dateiformat verwendet worden sein, das Sie ausgraben können, und MUSS mindestens ein Byte enthalten, das kein druckbares ASCII-Zeichen ist. In der PNG-Spezifikation erfahren Sie, wie Sie eine wirklich gründliche magische Zahl entwerfen . Im Quellcode des file(1)Befehls finden Sie eine Datenbank mit vorhandenen magischen Zahlen, die so umfassend ist, wie Sie wahrscheinlich finden.

    Der Sinn einer magischen Zahl besteht darin, die Datei bandintern mit ihrem Format eindeutig zu kennzeichnen. Wenn Sie keine magische Zahl angeben oder dies nicht das allererste Mal in der Datei ist, besteht die Gefahr, dass Programme Ihre Datei fälschlicherweise als einen anderen Dateityp identifizieren , was zu Datenverlust, Viren, die nicht erkannt werden , und anderem führt Katastrophen.

  • Eine Angabe der Version des Dateiformats. Auch wenn Sie der Meinung sind, dass Sie Ihr Dateiformat niemals drastisch überarbeiten müssen, sollten Sie die nächsten zwei Bytes nach der magischen Zahl eingeben 00 00und dokumentieren, dass dies eine 16-Bit-Versionsnummer in einer bestimmten Endianität ist (je nachdem, was Sie möchten, aber wählen Sie one und bleibe dabei in der gesamten Datei ) und wird inkrementiert, wenn sich die Bedeutung der nachfolgenden Daten radikal ändert. Dein zukünftiges Ich wird es dir danken.

    (Die PNG-Spezifikation schlägt hier einen anderen Weg ein und legt fest, dass Chunk-Formate eingefroren werden und dass alle zukünftigen Änderungen am Format die Form neuer Chunk-Typen annehmen. Dies gilt auch, ich empfehle jedoch den einfachen Magic Number + Versionsnummer-Ansatz für Anfänger in der Verarbeitung binärer Daten. Die Leute, die PNG entwickelt haben, haben auf jahrzehntelange Erfahrung mit Bildformaten zurückgegriffen.)

  • Eine Art Mechanismus zum Einbetten beliebiger Metadaten in die Datei. Dies kann so einfach sein, dass die nächsten zwei Bytes ein 16-Bit-Versatz vom Ende des Headers bis zum Beginn der eigentlichen Daten sind, wobei alles dazwischen als UTF-8-Schlüssel-Wert-Paare nach RFC 822 interpretiert wird (das heißt, " Tag: value\n" - wenn Sie diese Route wählen, empfehle ich, das Falten langer Linien nicht zuzulassen). Auch hier ist PNG wesentlich cleverer.

zwol
quelle
Es ist nicht erforderlich, ein eigenes Dateiformat zu erstellen. Speichern Sie die Daten einfach als Bild. Möglicherweise müssen Sie die Dimension ändern (z. B. 10k x 1k), damit dies unterstützt wird. Oder Sie könnten FITS verwenden . Wenn Ihre Daten komplexer sind als nur ein einzelnes Array, können Sie HDF , CDF oder NetCDF verwenden .
Joe
Ich würde vorschlagen, es einfach zu halten. 256 verschiedene Versionen werden ausreichen, und wenn nicht, können zusätzliche Versionen als Subversionen von Version 255 erstellt werden. Ebenso ist es für Metadaten ausreichend, sie in einer Version hinzuzufügen, wenn sie tatsächlich benötigt werden. @ Joe Image ??? Sie vermeiden die mögliche Formatverwirrung, indem Sie alle im Voraus verwirren!
Maaartinus
@maaartinus Wenn Sie das Versionsfeld mit zwei Bytes belegen, wird der Formatdesigner gezwungen, sich im Voraus auf eine Endianität festzulegen. Platz für Metadaten sollte immer in der Version 0 eines Binärformats sein, da Sie sonst mit schrecklichen Kludges wie ID3 enden. Ich habe großes Verständnis für die Logik der PNG-Spezifikation in Bezug auf die Erweiterbarkeit durch neue Chunk-Typen anstelle von Unebenheiten der Formatversion. Dateien mit Chunk-Struktur sind jedoch sehr komplex. Daher kann ich sie nur für einfache Fälle empfehlen. Ich war versucht, HDF als generisches Format zu empfehlen, das sich bereits mit vielen dieser Probleme befasst.
zwol
2

Unterschiedliche Architekturen haben unterschiedliche Darstellungen für ganze Zahlen. Das Hauptrisiko ist hier das Speichern der Byte - Darstellung einer ganze Zahl in einer Maschine und dann das zurück zu lesen versuchen , und den Inhalt als ganze Zahlen interpretieren in Maschine B. Wenn Maschinen A und B verschiedene Größen für ganze Zahlen und / oder unterschiedliche Endian , Sie‘ lle verursachen höchstwahrscheinlich undefiniertes Verhalten (z. B. in C) oder eine Ausnahme.

Da dies nur ein Programmierbeispiel und kein "echtes" Programm ist, ist es nicht wirklich ein Problem. Wenn dies ein echtes Programm wäre, wäre es normalerweise keine gute Idee, ein eigenes anwendungsspezifisches Binärformat zu rollen. Es gibt bessere Lösungen wie SQLite oder auf Zeichenfolgen basierende Serialisierungsformate wie JSON, YAML, XML usw. Für einzelne Werte würde es ausreichen, sie in eine Zeichenfolge umzuwandeln. Für einfache Listen können Sie eine Zeichenfolge pro Zeile speichern und die Eingabe in Zeilenumbrüche aufteilen, wenn Sie sie wieder einlesen.

Doval
quelle
Stimmen im Allgemeinen zu, aber JSON oder XML würden die Größe einer Datei mit 10 ^ 7 Zahlen erheblich erhöhen. Außerdem werden sie in der Regel auf einmal gelesen und analysiert. Das betreffende Kapitel befasst sich jedoch mit dem Sortieren von Dateien, die mehr Daten enthalten, als in den verfügbaren Speicher passen.
Caleb
Es hängt davon ab, was Sie tun. Manchmal ist der Performance-Hit von SQL im Vergleich zu einem Roll-Your-Own groß. Als ich es das letzte Mal gemacht habe, hatte ich kleine Platten und es bestand eine große Chance, dass ich Nachbarn haben wollte. Das Lesen eines größeren Blocks von der Festplatte kostet im Allgemeinen fast nichts. Wenn ich also einen Datensatz haben möchte, lese ich 1000 in einen Cache. Meine Aufzeichnungen lagen mit ziemlicher Sicherheit nebeneinander, und mit SQL würde der Plattenkopf überall herumspringen.
Loren Pechtel