Python come scrivere su un file binario?


128

Ho un elenco di byte come numeri interi, che è qualcosa di simile

[120, 3, 255, 0, 100]

Come posso scrivere questo elenco in un file come binario?

Funzionerebbe?

newFileBytes = [123, 3, 255, 0, 100]
# make file
newFile = open("filename.txt", "wb")
# write to file
newFile.write(newFileBytes)

60
Chiedete "Funzionerebbe?". L'hai provato?
StephenTG

1
Dovrebbe essere TypeError: argument 1 must be string or buffer, not list.
anatoly techtonik

Risposte:


128

Questo è esattamente ciò che bytearrayserve:

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

Se stai usando Python 3.x, puoi usare bytesinvece (e probabilmente dovresti, poiché segnala meglio la tua intenzione). Ma in Python 2.x, questo non funzionerà, perché bytesè solo un alias per str. Come al solito, mostrare con l'interprete interattivo è più facile che spiegare con il testo, quindi fammelo fare.

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]'

1
Buon uso dei tipi incorporati. Basta notare che bytearray è stato aggiunto nella 2.6, se vuoi supportare i sistemi legacy, dovrebbe essere evitato.
Perkins

7
@ Perkins: certo, e dovresti evitare le espressioni del generatore se devi lavorare su 2.3, fai attenzione con entrambi str.encodee struct.packse devi lavorare su 2.2. Ma 2.6 è uscito da 5 anni ormai; tutti e tre gli Ubuntu LTS ancora in supporto, tutte e tre le versioni di OS X in supporto, la precedente versione principale di CentOS / RHEL, ecc., vengono tutti con esso integrato. Se è necessario supportare 2.5 o 2.1 o 1.6 o altro, probabilmente sapere ...
abarnert

4
Con Python 2 su Windows, ho scoperto che scrivere a viene bytearraycomunque convertito \nin \r\n, rendendolo insoddisfacente per i dati binari, se il flag "b" non viene passato all'apertura del file.
feersum

6
@feersum: Certamente; questo è ciò che significa modalità binaria e testo in 2.x. Non importa da che tipo provengono i tuoi byte. (In 3.x, ovviamente, la modalità binario contro testo significa che scrivi byte contro unicode e la \r\nfunzione fa parte delle opzioni universali di nuova riga per il testo.)
abarnert

Non sono sicuro che bytearray () sia una buona scelta per la scrittura di file. Dovresti limitare le dimensioni a blocchi gestibili. Altrimenti, una volta che le dimensioni dei file diventano troppo elevate, la memoria verrà esaurita.
mckenzm

31

Utilizzare struct.packper convertire i valori interi in byte binari, quindi scrivere i byte. Per esempio

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

Tuttavia non darei mai un file binario a .txt un'estensione a .

Il vantaggio di questo metodo è che funziona anche per altri tipi, ad esempio se uno qualsiasi dei valori fosse maggiore di 255, potresti usare '5i'per il formato invece di ottenere interi a 32 bit.


.txt va bene se hai un modo per sapere che i dati che stai scrivendo rientrano tutti nell'intervallo ascii stampabile. Tuttavia, penso che tu abbia ragione in questo caso, poiché i dati di esempio includono caratteri non stampabili.
Perkins

@ Perkins Non ho ipotizzato che i valori sarebbero addirittura inferiori a 256 molto meno nell'intervallo ASCII. Anche se lo sono, i file .txt dovrebbero essere riservati a quelli che hanno senso per un essere umano che non si applica mai ai dati binari.
Mark Ransom

1
Hai ragione, struct.pack è anche la strada da percorrere se stai scrivendo dati con valori superiori a 255, poiché né bytearray né chr possono gestire valori interi più grandi.
Perkins

1
@MarkRansom: Bene, questa è sicuramente una buona soluzione al problema più generale di "Ho un elenco di numeri interi di qualche dimensione arbitraria ma fissa, come posso scriverli in un file binario?" e posso vedere persone che cercano quella domanda e trovano questa ...
abarnert

1
struct.pack è la risposta migliore; è molto più flessibile della semplice creazione di un cestello.
Seth

12

Per convertire da numeri interi <256 a binari, utilizzare la chrfunzione. Quindi stai cercando di fare quanto segue.

newFileBytes=[123,3,255,0,100]
newfile=open(path,'wb')
newfile.write((''.join(chr(i) for i in newFileBytes)).encode('charmap'))

1
Devi indicare <128. Come si lamenta python3: UnicodeEncodeError: il codec "ascii" non può codificare il carattere "\ x89" nella posizione 0: ordinale non compreso nell'intervallo (128)
idoneo

2
No, intendo <256, ma la codifica dovrebbe essere charmapinvece di ascii, e funziona in python2 così come in python3. La asciicodifica funziona solo in python2.
Perkins

9

A partire da Python 3.2+, puoi anche farlo usando il to_bytesmetodo int nativo:

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'))

Cioè, ogni singola chiamata a to_bytesin questo caso crea una stringa di lunghezza 1, con i suoi caratteri disposti in ordine big-endian (che è banale per le stringhe di lunghezza 1), che rappresenta il valore intero byte. Puoi anche accorciare le ultime due righe in una sola:

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

8

È possibile utilizzare il seguente esempio di codice utilizzando la sintassi di Python 3:

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

Ecco la shell one-liner:

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

0

Usa pickle, in questo modo: import pickle

Il tuo codice sarebbe simile a questo:

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

Per rileggere i dati, utilizzare il metodo pickle.load


3
Questo non produce un file binario di 5 byte di lunghezza, dove l'unico contenuto è 120, 3, 255, 0, 100. In un sistema chiuso, questo può essere accettabile.
parvus
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.