Python como gravar em um arquivo binário?


128

Eu tenho uma lista de bytes como números inteiros, que é algo como

[120, 3, 255, 0, 100]

Como posso escrever esta lista em um arquivo como binário?

Isso funcionaria?

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

60
Você pergunta "Isso funcionaria?". Tentaste?
precisa saber é o seguinte

1
Deveria ser TypeError: argument 1 must be string or buffer, not list.
Anatoly techtonik

Respostas:


128

É exatamente bytearraypara isso:

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

Se você estiver usando o Python 3.x, poderá usá-lo bytes(e provavelmente deveria, pois sinaliza melhor sua intenção). Mas no Python 2.x, isso não funcionará, porque bytesé apenas um alias para str. Como de costume, mostrar com o intérprete interativo é mais fácil do que explicar com texto, então deixe-me fazer isso.

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
Bom uso de tipos embutidos. Observe que o bytearray foi adicionado no 2.6. Se você deseja oferecer suporte a sistemas legados, isso deve ser evitado.
Perkins

7
@ Perkins: Claro, e você deve evitar expressões geradoras se precisar trabalhar no 2.3, tenha cuidado com os dois str.encodee struct.packse precisar trabalhar no 2.2. Mas o 2.6 está fora há 5 anos; todos os três LTSs do Ubuntu ainda suportam, todas as três versões do OS X, a versão principal anterior do CentOS / RHEL, etc., todos vêm com ele embutido. Se você precisar suportar o 2.5 ou 2.1 ou 1.6 ou o que for, provavelmente você sabe ...
abarnert 21/08

4
Com o Python 2 no Windows, descobri que escrever um bytearrayainda se converte \nem \r\n, tornando-o insatisfatório para dados binários, se o sinalizador "b" não for passado ao abrir o arquivo.
feersum

6
@feersum: Claro; é isso que o modo binário versus modo de texto significa na 2.x. Não importa de que tipo seus bytes vêm. (Em 3.x, é claro, significa em modo texto vs. binários que você escreve bytes vs. unicode, eo \r\nrecurso faz parte das opções newlines universais para o texto.)
abarnert

Não tenho certeza se bytearray () é uma boa opção para a gravação de arquivos. Você precisaria limitar o tamanho a pedaços gerenciáveis. Caso contrário, quando o tamanho do arquivo ficar muito alto, você ficará sem memória.
Mckenzm 02/05/19

30

Use struct.packpara converter os valores inteiros em bytes binários e, em seguida, escreva os bytes. Por exemplo

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

No entanto, eu nunca daria uma .txtextensão a um arquivo binário .

O benefício desse método é que ele também funciona para outros tipos, por exemplo, se algum dos valores for maior que 255, você poderá usar '5i'o formato para obter números inteiros de 32 bits.


.txt é bom se você tiver alguma maneira de saber que os dados que você está gravando estão dentro do intervalo ascii imprimível. No entanto, você está correto, acho que neste caso, uma vez que os dados de exemplo incluem caracteres não imprimíveis.
Perkins

@ Perkins Eu não assumi que os valores seriam inferiores a 256 e muito menos no intervalo ASCII. Mesmo se forem, os arquivos .txt devem ser reservados para aqueles que fazem sentido para um humano que nunca se aplica a dados binários.
Mark Ransom

1
Você está certo, struct.pack também é o caminho a seguir, se você estiver gravando dados com valores acima de 255, pois nem bytearray nem chr podem lidar com valores inteiros maiores.
Perkins

1
@ MarkRansom: Bem, essa ainda é definitivamente uma boa solução para o problema mais geral de "Eu tenho uma lista de números inteiros de algum tamanho arbitrário, mas fixo, como posso escrevê-los em um arquivo binário?" e eu posso ver as pessoas a procurar a essa pergunta e encontrar este ...
abarnert

1
struct.pack é a melhor resposta; é muito mais flexível do que simplesmente criar um bytearray.
Seth

12

Para converter de números inteiros <256 para binários, use a chrfunção Então você está olhando para fazer o seguinte.

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

1
Você deve significar <128. Como o python3 reclama: UnicodeEncodeError: o codec 'ascii' não pode codificar o caractere '\ x89' na posição 0: ordinal fora do intervalo (128)
qualificado em

2
Não, quero dizer <256, mas a codificação deve ser, charmape não ascii, e funciona tanto em python2 quanto em python3. A asciicodificação funciona apenas em python2.
Perkins

9

No Python 3.2+, você também pode fazer isso usando o 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'))

Ou seja, cada chamada única to_bytesnesse caso cria uma sequência de comprimento 1, com seus caracteres organizados em ordem big endian (que é trivial para as sequências de comprimento 1), que representa o valor inteiro byte. Você também pode reduzir as duas últimas linhas em uma única:

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

8

Você pode usar o seguinte exemplo de código usando a sintaxe do Python 3:

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

Aqui está o 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])))'

1

Use pickle, assim: import pickle

Seu código ficaria assim:

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

Para ler os dados novamente, use o método pickle.load


3
Isso não produz um arquivo binário de 5 bytes de comprimento, onde o único conteúdo é 120, 3, 255, 0, 100. Em um sistema fechado, isso pode ser aceitável.
parvus
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.