Python, wie man in eine Binärdatei schreibt?

128

Ich habe eine Liste von Bytes als Ganzzahlen, was so etwas wie ist

[120, 3, 255, 0, 100]

Wie kann ich diese Liste als Binärdatei in eine Datei schreiben?

Würde das funktionieren?

newFileBytes = [123, 3, 255, 0, 100]
# make file
newFile = open("filename.txt", "wb")
# write to file
newFile.write(newFileBytes)
Aaron Hiniker
quelle
60
Sie fragen "Würde das funktionieren?". Hast du es versucht?
StephenTG
1
Sollte sein TypeError: argument 1 must be string or buffer, not list.
Anatoly Techtonik

Antworten:

127

Genau dafür bytearrayist gedacht:

newFileByteArray = bytearray(newFileBytes)
newFile.write(newFileByteArray)

Wenn Sie Python 3.x verwenden, können Sie bytesstattdessen verwenden (und sollten dies wahrscheinlich tun, da dies Ihre Absicht besser signalisiert). Aber in Python 2.x funktioniert das nicht, weil byteses nur ein Alias ​​für ist str. Wie üblich ist das Zeigen mit dem interaktiven Interpreter einfacher als das Erklären mit Text. Lassen Sie mich das einfach tun.

Python 3.x:

>>> bytearray(newFileBytes)
bytearray(b'{\x03\xff\x00d')
>>> bytes(newFileBytes)
b'{\x03\xff\x00d'

Python 2.x:

>>> bytearray(newFileBytes)
bytearray(b'{\x03\xff\x00d')
>>> bytes(newFileBytes)
'[123, 3, 255, 0, 100]'
abarnert
quelle
1
Gute Verwendung von eingebauten Typen. Beachten Sie nur, dass Bytearray in 2.6 hinzugefügt wurde. Wenn Sie Legacy-Systeme unterstützen möchten, sollte dies vermieden werden.
Perkins
7
@Perkins: Sicher, und Sie sollten Generatorausdrücke vermeiden, wenn Sie an 2.3 arbeiten müssen, seien Sie vorsichtig mit beiden str.encodeund struct.packwenn Sie an 2.2 arbeiten müssen. Aber 2.6 ist seit 5 Jahren nicht mehr erhältlich. Alle drei noch unterstützten Ubuntu-LTSs, alle drei unterstützten OS X-Versionen, die vorherige Hauptversion von CentOS / RHEL usw. sind bereits integriert. Wenn Sie 2.5 oder 2.1 oder 1.6 oder was auch immer unterstützen müssen, sind Sie wahrscheinlich weiß ...
abarnert
4
Mit Python 2 unter Windows, fand ich , dass ein Schreiben bytearraynoch Konvertiten \nzu \r\n, es unbefriedigend für binäre Daten zu machen, wenn die „b“ Flag nicht bestanden, wenn die Datei geöffnet wird .
Feersum
6
@feersum: Natürlich; Das ist es, was der Binär- oder Textmodus in 2.x bedeutet . Es spielt keine Rolle, von welchem ​​Typ Ihre Bytes stammen. (In 3.x bedeutet der Binär- / Textmodus natürlich, dass Sie Bytes gegen Unicode schreiben, und die \r\nFunktion ist Teil der universellen Zeilenumbruchoptionen für Text.)
Abarnert
Ich bin mir nicht sicher, ob bytearray () eine gute Wahl für das Schreiben von Dateien ist. Sie müssten die Größe auf überschaubare Blöcke beschränken. Andernfalls wird Ihnen der Speicher ausgehen, sobald Ihre Dateigrößen zu hoch werden.
McKenzm
30

Verwenden Sie struct.packdiese Option , um die Ganzzahlwerte in Binärbytes zu konvertieren, und schreiben Sie dann die Bytes. Z.B

newFile.write(struct.pack('5B', *newFileBytes))

Allerdings würde ich niemals eine Binärdatei a geben .txt Erweiterung geben.

Der Vorteil dieser Methode besteht darin, dass sie auch für andere Typen funktioniert. Wenn beispielsweise einer der Werte größer als 255 ist, können Sie '5i'stattdessen das Format verwenden, um vollständige 32-Bit-Ganzzahlen zu erhalten.

Mark Ransom
quelle
.txt ist in Ordnung, wenn Sie wissen möchten, dass die Daten, die Sie schreiben, alle in den druckbaren ASCII-Bereich fallen. Ich denke jedoch, dass Sie in diesem Fall richtig sind, da die Beispieldaten nicht druckbare Zeichen enthalten.
Perkins
@Perkins Ich bin nicht davon ausgegangen, dass die Werte im ASCII-Bereich sogar unter 256 liegen würden. Selbst wenn dies der Fall ist, sollten TXT-Dateien für diejenigen reserviert werden, die für einen Menschen sinnvoll sind und niemals für Binärdaten gelten.
Mark Ransom
1
Sie haben Recht, struct.pack ist auch der richtige Weg, wenn Sie Daten mit Werten über 255 schreiben möchten, da weder bytearray noch chr größere ganzzahlige Werte verarbeiten können.
Perkins
1
@MarkRansom: Nun, dies ist definitiv immer noch eine gute Lösung für das allgemeinere Problem: "Ich habe eine Liste von Ganzzahlen beliebiger, aber fester Größe. Wie kann ich sie in eine Binärdatei schreiben?" und ich kann Leute sehen, die nach dieser Frage suchen und diese finden ...
abarnert
1
struct.pack ist die bessere Antwort; Es ist weitaus flexibler als nur ein Bytearray zu erstellen.
Seth
12

Verwenden Sie die chrFunktion, um von Ganzzahlen <256 in Binärzahlen zu konvertieren . Sie möchten also Folgendes tun.

newFileBytes=[123,3,255,0,100]
newfile=open(path,'wb')
newfile.write((''.join(chr(i) for i in newFileBytes)).encode('charmap'))
Perkins
quelle
1
Sie müssen <128 bedeuten. Wie Python3 sich beschwert: UnicodeEncodeError: Der Codec 'ascii' kann das Zeichen '\ x89' an Position 0 nicht codieren: Ordnungszahl nicht im Bereich (128)
Elig
2
Nein, ich meine <256, aber die Codierung sollte charmapeher als sein asciiund funktioniert sowohl in Python2 als auch in Python3. Die asciiCodierung funktioniert nur in Python2.
Perkins
9

Ab Python 3.2+ können Sie dies auch mit der to_bytesnativen int-Methode erreichen:

newFileBytes = [123, 3, 255, 0, 100]
# make file
newFile = open("filename.txt", "wb")
# write to file
for byte in newFileBytes:
    newFile.write(byte.to_bytes(1, byteorder='big'))

Das heißt, jeder einzelne Aufruf von to_bytesin diesem Fall erzeugt eine Zeichenfolge der Länge 1, deren Zeichen in Big-Endian-Reihenfolge angeordnet sind (was für Zeichenfolgen der Länge 1 trivial ist), die den ganzzahligen Wert darstellt byte. Sie können auch die letzten beiden Zeilen zu einer einzigen verkürzen:

newFile.write(''.join([byte.to_bytes(1, byteorder='big') for byte in newFileBytes]))
CrepeGoat
quelle
8

Sie können das folgende Codebeispiel mit der Python 3-Syntax verwenden:

from struct import pack
with open("foo.bin", "wb") as file:
  file.write(pack("<IIIII", *bytearray([120, 3, 255, 0, 100])))

Hier ist Shell Einzeiler:

python -c $'from struct import pack\nwith open("foo.bin", "wb") as file: file.write(pack("<IIIII", *bytearray([120, 3, 255, 0, 100])))'
Kenorb
quelle
1

Verwenden Sie Gurke wie folgt: Importgurke

Ihr Code würde folgendermaßen aussehen:

import pickle
mybytes = [120, 3, 255, 0, 100]
with open("bytesfile", "wb") as mypicklefile:
    pickle.dump(mybytes, mypicklefile)

Verwenden Sie die Methode pickle.load, um die Daten zurückzulesen

Raymond Mlambo
quelle
3
Dies erzeugt keine Binärdatei mit einer Länge von 5 Bytes, wobei der einzige Inhalt 120, 3, 255, 0, 100 ist. In einem geschlossenen System kann dies jedoch akzeptabel sein.
Parvus