Lesen und Schreiben von binären Daten

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
sashs
User
Beiträge: 10
Registriert: Dienstag 19. Februar 2013, 21:35

Hallo,

ich möchte eine binäre Datei einmal komplett in den Speicher lesen. Die Datei lese ich natürlich mit 'rb' ein. Die Daten verarbeite ich dann mit ctypes und Pointern weiter. Das funktioniert alles. Ich wollte jetzt Daten ändern und es zurück schreiben. Da ich auf den eingelesenen Daten arbeite sollte das eigentlich kein Problem sein.

Mein Problem ist jetzt, wenn ich mir die Länge der Daten nach dem Einlesen abfrage, 3 angezeigt wird, obwohl alles eingelesen wird und es 400kb sind. Wenn ich jetzt die gleichen Daten schreibe, werden natürlich nur 3 Byte in die neue Datei geschrieben und das ist schlecht. Das alles eingelesen wird, sehe ich da alle Daten komplett angezeigt werden. Das Problem werden wahrscheinlich die Nullbytes sein, die sich in der Datei befinden.

Jetzt fehlt mir eine Idee wie ich weiter vorgehen kann.


Viele Grüße

Sascha
Benutzeravatar
/me
User
Beiträge: 3556
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

sashs hat geschrieben:Das Problem werden wahrscheinlich die Nullbytes sein, die sich in der Datei befinden.
Die Nullbytes werden es eher nicht sein. Der folgende Code (Python 3) schreibt problemlos 10000 Nullbytes weg.

Code: Alles auswählen

def main():
    data = b'\0' * 10000
    with open('filewriter.dat', 'wb') as fp:
        fp.write(data)

if __name__ == '__main__':
    main()
Stellst du bei dir sicher, dass die Datei sauber geschlossen wird?
Hellstorm
User
Beiträge: 231
Registriert: Samstag 22. Juni 2013, 15:01

Oder kann es sein dass sich der Zeiger in der Datei am Ende befindet? Den müsstest du vielleicht zurücksetzen. Das Problem hatte ich auch einmal (Wobei dann 0 Bytes gespeichert wurden und keine 3).
BlackJack

@sashs: Das kann nicht sein. Gegeben folgender Quelltext:

Code: Alles auswählen

    with open('data.bin', 'rb') as data_file:
        data = data_file.read()
    print len(data)
Wenn die Datei ``data.bin`` nun 400 KiB gross ist, wird die Ausgabe niemals 3 sein. Also was machst Du beim einlesen anders? Da muss dann der Fehler sein.

Und wie änderst Du die Daten? Das was Du da eingelesen hast ist unveränderbar. Du benutzt `ctypes` doch hoffentlich nicht um da tatsächlich direkt in dem Speicher des Python-Objekts herum zu pfuschen!?

Ansonsten wäre ein minimales, lauffähiges Beispiel welches das Problem aufweist, mit Beispieldaten, praktisch, damit andere das Problem nachvollziehen können.
sashs
User
Beiträge: 10
Registriert: Dienstag 19. Februar 2013, 21:35

Ja, das habe ich auch gesehen. Hatte nicht direkt eine Variable dafür angelegt, sondern das in ein ctypes-Array geschrieben und da ist der Fehler passiert, habe ich gerade gesehen. Da wurde dann auch die falsche Länge ausgegeben.

Schlecht, wenn man direkt zwei Sachen mischt.

Aber vielen Dank für eure Hilfe.


Viele Grüße

Sascha
Benutzeravatar
MagBen
User
Beiträge: 799
Registriert: Freitag 6. Juni 2014, 05:56
Wohnort: Bremen
Kontaktdaten:

sashs hat geschrieben:ich möchte eine binäre Datei einmal komplett in den Speicher lesen. Die Datei lese ich natürlich mit 'rb' ein
So natürlich ist das gar nicht mal. Hast Du Dir schon mal numpy.memmap (http://docs.scipy.org/doc/numpy/referen ... emmap.html) angeschaut? Diese Funktion erzeugt ein Array-Objekt, das intern auf die Dateidaten zugreift. Du kannst wählen wie die Dateidaten interpretiert werden sollen (byte, int16, int32, int64, float32, float64, ...) und kannst diese Interpretation jederzeit ändern. Du hast den komfortablen Array-Zugriff auf die Daten und am Ende (wenn das Array-Objekt gelöscht wird) wird der Inhalt in die Datei geschrieben.
a fool with a tool is still a fool, www.magben.de, YouTube
BlackJack

Da muss man nicht unbedingt Numpy als Abhängigkeit reinholen. Das `mmap`-Modul aus der Standardbibliothek sollte auch gehen.
Benutzeravatar
MagBen
User
Beiträge: 799
Registriert: Freitag 6. Juni 2014, 05:56
Wohnort: Bremen
Kontaktdaten:

BlackJack hat geschrieben:Das `mmap`-Modul aus der Standardbibliothek sollte auch gehen.
Das scheint mir eher für Text-Dateien gemacht und nicht so sehr für Binär-Dateien.
a fool with a tool is still a fool, www.magben.de, YouTube
BlackJack

@MagBen: Ähm, das geht gar nicht für Dateien im Textmodus. Und Numpy benutzt das intern ja auch.
Benutzeravatar
MagBen
User
Beiträge: 799
Registriert: Freitag 6. Juni 2014, 05:56
Wohnort: Bremen
Kontaktdaten:

Man kann tatsächlich Text-Dateien im Binärmodus lesen und schreiben. So würde ich jedenfalls das Beispiel aus der Python-API interpretieren: https://docs.python.org/2/library/mmap.html. Dort wird gezeigt wie man den Text "Hello world!" mit memmap aus einer Datei lesen kann.

Und natürlich sind Textdateien ja irgendwie binär, sonst könnten sie ja nicht auf der Platte gespeichert werden. Aber ich kenne den Ausdruck Binärdatei eher für soetwas wie z.B. eine gif-Datei. Und mal angenommen man müsste soetwas wie eine gif-Datei selbst lesen und schreiben, dann braucht's dafür ohne numpy.memmap mehr Code als mit numpy.memmap. Denn mit dem memmap aus der Python Standardbibliothek habe ich fürs Datei-IO nur Byte und String als Datentypen. Lies mal die nächsten 4 Byte als int und die übernächsten 3 x 2 Byte als RGB Wert damit, das ergibt dann einige Zeilen Code.

Natürlich gibt's schon irgendwo eine Bibliothek zum Lesen und Schreiben von allen möglichen Grafik-Dateiformaten, das Ganze ist nur als anschauliches Beispiel gedacht.
a fool with a tool is still a fool, www.magben.de, YouTube
BlackJack

@MagBen: Das Beispiel benutzt halt „lesbare” Bytewerte, aber was man da mappt sind Bytes und die muss man auch als Bytes verarbeiten, und die Datei *muss* als Binärdatei geöffnet werden, da darf beim Lesen und Schreiben nichts verändert werden, insbesondere nicht bei der Länge der Bytes was Windows ja zum Beispiel bei Zeilenende-Zeichen macht, und was bei Kodierungen wie UTF-* vorkommen kann. Textdateien kann man damit also nur sehr unzureichend mit Einschränkungen verarbeiten. Eigentlich nur wenn man so etwas wie „fixed length records” hat, aber das würde ich dann auch schon nicht mehr wirklich als normale Textdatei ansehen, weil man die nicht mehr so leicht mit einem Texteditor bearbeiten kann ohne sich die ziemlich leicht zu zerschiessen wenn man nicht höllisch aufpasst.

`mmap` hat seine Stärken wenn man grosse Dateien hat, von denen man nicht alles oder zumindest nicht alles *linear* verarbeiten will. Man muss also in diesen Dateien bestimmte Stellen annavigieren können von denen man vorher weiss an welchem Offset sie beginnen und wie gross sie sind. Das klingt alles nicht nach Textdateien.

Andere numerische Datentypen als Bytes kann man zum Beispiel mit dem `struct`-Modul auslesen. Ein 4-Byte-Integer an Offset 10 und 6 Bytewerte ab Offset 14 zum Beispiel so:

Code: Alles auswählen

In [51]: f = open('test.db', 'rb')

In [52]: mm = mmap.mmap(f.fileno(), 0)

In [53]: struct.unpack_from('I', mm, 10)
Out[53]: (544498029,)

In [54]: struct.unpack_from('6B', mm, 14)
Out[54]: (51, 0, 4, 0, 1, 1)
Wenn man weiss das beides direkt hintereinander kommt, dann kann man das auch in einem Aufruf abhandeln. Und wenn ich das richtig verstanden habe möchte der OP C-Structuren ”über die Daten legen”. Damit lassen sich dann Sachen machen bei denen der Numpy-Code komplizierter und unübersichtlicher wird.
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

MagBen hat geschrieben:... und am Ende (wenn das Array-Objekt gelöscht wird) wird der Inhalt in die Datei geschrieben.
Nur damit nichts Falsches unkommentiert stehen bleibt: Werden Daten geändert, sind sie sofort für jeden, der die Datei liest, sichtbar und nicht erst, wenn die Datei wieder geschlossen wird. mmap ist daher auch eine Methode um shared memory zu realisieren, also Speicher, der von mehren Prozessen geteilt wird.
Benutzeravatar
MagBen
User
Beiträge: 799
Registriert: Freitag 6. Juni 2014, 05:56
Wohnort: Bremen
Kontaktdaten:

@BlackJack: sehr schönes Beispiel
Sirius3 hat geschrieben:Werden Daten geändert, sind sie sofort für jeden, der die Datei liest, sichtbar
Sie können, aber sie müssen nicht, schließlich werden die Daten ja gecached. Und nicht umsonst steht in der API Doku http://docs.scipy.org/doc/numpy/referen ... emmap.html
"Deletion flushes memory changes to disk before removing the object"
a fool with a tool is still a fool, www.magben.de, YouTube
BlackJack

@MagBen: Doch die Daten sind *sofort* für jeden sichtbar. Denn wenn die Datei von jemand anderem geöffnet wird und er so einen veränderten Datenteil lesen will, dann bekommt der ja die geänderten Daten aus dem Cache und nicht die noch unveränderten Daten von der Festplatte.

Ich wäre an der Stelle auch vorsichtig der Dokumentation von Numpy zu ”glauben”. Ich gehe davon aus das Numpy am Ende ein `flush()` aufruft, beziehungsweise das implizit im `close()` enthalten ist, das bedeutet aber *nicht* das die Daten dadurch tatsächlich auf die Platte geschrieben werden. Du hast den Cache ja selbst erwähnt. `flush()` ist nicht gleich `fsync()`.
Antworten