Python cómo escribir en un archivo binario?

128

Tengo una lista de bytes como enteros, que es algo así como

[120, 3, 255, 0, 100]

¿Cómo puedo escribir esta lista en un archivo como binario?

¿Funcionaría esto?

newFileBytes = [123, 3, 255, 0, 100]
# make file
newFile = open("filename.txt", "wb")
# write to file
newFile.write(newFileBytes)
Aaron Hiniker
fuente
6060
Usted pregunta "¿Funcionaría esto?". ¿Lo has probado?
StephenTG
1
Debe ser TypeError: argument 1 must be string or buffer, not list.
anatoly techtonik

Respuestas:

128

Esto es exactamente para lo que bytearraysirve:

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

Si está usando Python 3.x, puede usarlo bytesen su lugar (y probablemente debería hacerlo, ya que indica mejor su intención). Pero en Python 2.x, eso no funcionará, porque byteses solo un alias para str. Como de costumbre, mostrar con el intérprete interactivo es más fácil que explicar con texto, así que déjame hacer eso.

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
fuente
1
Buen uso de los tipos incorporados. Solo tenga en cuenta que bytearray se agregó en 2.6, si desea admitir sistemas heredados, debe evitarlo.
Perkins
77
@Perkins: Claro, y debe evitar las expresiones generadoras si necesita trabajar en 2.3, tenga cuidado con ambas str.encodey struct.packsi necesita trabajar en 2.2. Pero 2.6 ha estado fuera por 5 años; los tres Ubuntu LTS todavía están en soporte, las tres versiones de OS X en soporte, la versión principal anterior de CentOS / RHEL, etc., todos vienen con él incorporado. Si necesita soportar 2.5 o 2.1 o 1.6 o lo que sea, probablemente saber ...
abarnert
44
Con Python 2 en Windows, he encontrado que la escritura de un bytearrayconvertidos todavía \na \r\n, por lo que es poco satisfactoria para los datos binarios, si la bandera "b" no se pasa al abrir el archivo.
feersum
66
@feersum: Por supuesto; eso es lo que significa el modo binario vs. texto en 2.x. No importa de qué tipo provienen sus bytes. (En 3.x, por supuesto, los medios binarios en modo texto vs. que escriben bytes frente a Unicode y la \r\nfunción forma parte de las opciones de saltos de línea universales para el texto.)
abarnert
No estoy seguro de que bytearray () sea una buena opción para escribir archivos. Debería limitar el tamaño a fragmentos manejables. De lo contrario, una vez que su tamaño de archivo sea demasiado alto, se quedará sin memoria.
mckenzm
31

Use struct.packpara convertir los valores enteros en bytes binarios, luego escriba los bytes. P.ej

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

Sin embargo, nunca le daría una .txtextensión a un archivo binario .

El beneficio de este método es que también funciona para otros tipos, por ejemplo, si alguno de los valores fuera mayor que 255, podría usar '5i'el formato para obtener enteros completos de 32 bits.

Mark Ransom
fuente
.txt está bien si tiene alguna forma de saber que los datos que está escribiendo están dentro del rango de ASCII imprimible. Sin embargo, creo que tiene razón en este caso, ya que los datos de ejemplo incluyen caracteres no imprimibles.
Perkins
@Perkins No asumí que los valores serían incluso menos de 256 mucho menos en el rango ASCII. Incluso si lo son, los archivos .txt deben reservarse para aquellos que tienen sentido para un ser humano que nunca se aplica a datos binarios.
Mark Ransom
1
Tiene razón, struct.pack también es el camino a seguir si va a escribir datos con valores superiores a 255, ya que ni bytearray ni chr pueden manejar valores enteros más grandes.
Perkins
1
@MarkRansom: Bueno, esta es definitivamente una buena solución para el problema más general de "Tengo una lista de enteros de algún tamaño arbitrario pero fijo, ¿cómo puedo escribirlos en un archivo binario?" y puedo ver personas buscando esa pregunta y encontrando esta ...
abarnert
1
struct.pack es la mejor respuesta; es mucho más flexible que simplemente crear un bytearray.
Seth
12

Para convertir de enteros <256 a binario, use la chrfunción. Entonces estás buscando hacer lo siguiente.

newFileBytes=[123,3,255,0,100]
newfile=open(path,'wb')
newfile.write((''.join(chr(i) for i in newFileBytes)).encode('charmap'))
Perkins
fuente
1
Debe significar <128. Como python3 se queja: UnicodeEncodeError: el códec 'ascii' no puede codificar el carácter '\ x89' en la posición 0: ordinal no está en el rango (128)
elegible el
2
No, quiero decir <256, pero la codificación debería ser charmapmás que ascii, y funciona tanto en python2 como en python3. La asciicodificación solo funciona en python2.
Perkins
9

A partir de Python 3.2+, también puede lograr esto utilizando el to_bytesmétodo 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'))

Es decir, cada llamada a to_bytesen este caso crea una cadena de longitud 1, con sus caracteres dispuestos en orden big-endian (que es trivial para cadenas de longitud 1), que representa el valor entero byte. También puede acortar las dos últimas líneas en una sola:

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

Puede usar el siguiente código de ejemplo usando la sintaxis de Python 3:

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

Aquí hay una línea de shell:

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

Use pickle, así: import pickle

Su código se vería así:

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

Para volver a leer los datos, use el método pickle.load

Raymond Mlambo
fuente
3
Esto no produce un archivo binario de 5 bytes de longitud, donde el único contenido es 120, 3, 255, 0, 100. Sin embargo, en un sistema cerrado, esto puede ser aceptable.
parvus