Python как записать в двоичный файл?

129

У меня есть список байтов как целых чисел, что-то вроде

[120, 3, 255, 0, 100]

Как я могу записать этот список в файл как двоичный?

Это сработает?

newFileBytes = [123, 3, 255, 0, 100]
# make file
newFile = open("filename.txt", "wb")
# write to file
newFile.write(newFileBytes)
Аарон Хиникер
источник
61
Вы спросите: «Сработает ли это?». Ты это пробовал?
StephenTG
1
Должно быть TypeError: argument 1 must be string or buffer, not list.
анатолий техтоник 09

Ответы:

130

Это как раз то bytearray, для чего:

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

Если вы используете Python 3.x, вы можете использовать bytesвместо него (и, вероятно, должны, поскольку это лучше сигнализирует о ваших намерениях). Но в Python 2.x это не сработает, потому что bytesэто просто псевдоним для str. Как обычно, показывать с помощью интерактивного интерпретатора проще, чем объяснять с помощью текста, поэтому позвольте мне просто сделать это.

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
источник
1
Хорошее использование встроенных типов. Просто обратите внимание, что bytearray был добавлен в 2.6, если вы хотите поддерживать устаревшие системы, этого следует избегать.
Perkins
7
@Perkins: Конечно, и вам следует избегать выражений генератора, если вам нужно работать с 2.3, будьте осторожны с обоими, str.encodeи struct.packесли вам нужно работать с 2.2. Но 2.6 отсутствует уже 5 лет; все три Ubuntu LTS все еще поддерживаются, все три версии OS X в поддержке, предыдущая основная версия CentOS / RHEL и т. д. - все они уже встроены. Если вам нужно поддерживать 2.5, 2.1 или 1.6 или что-то еще, вы, вероятно, знаю…
abarnert
4
С Python 2 в Windows я обнаружил, что запись a по- bytearrayпрежнему преобразуется \nв \r\n, что делает его неудовлетворительным для двоичных данных, если флаг «b» не передается при открытии файла.
feersum
6
@feersum: Конечно; вот что означает двоичный и текстовый режим в 2.x. Неважно, какого типа ваши байты. (В 3.x, конечно, двоичный или текстовый режим означает, что вы пишете байты вместо юникода, и эта \r\nфункция является частью универсальных опций новой строки для текста.)
abarnert
Я не уверен, что bytearray () - хороший выбор для записи файлов. Вам нужно будет ограничить размер управляемыми фрагментами. В противном случае, когда размер ваших файлов станет слишком большим, вам не хватит памяти.
mckenzm
33

Используйте struct.packдля преобразования целочисленных значений в двоичные байты, затем запишите байты. Например

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

Однако я бы никогда не дал бинарному файлу .txtрасширение.

Преимущество этого метода заключается в том, что он работает и для других типов, например, если какое-либо из значений было больше 255, вы могли бы использовать его '5i'для формата вместо того, чтобы получить полные 32-битные целые числа.

Марк Рэнсом
источник
.txt подойдет, если у вас есть способ узнать, что все данные, которые вы пишете, попадают в печатаемый диапазон ascii. Однако я думаю, что в этом случае вы правы, поскольку данные примера содержат непечатаемые символы.
Perkins
@Perkins Я не предполагал, что значения будут даже меньше 256, намного меньше в диапазоне ASCII. Даже если это так, файлы .txt должны быть зарезервированы для тех, которые имеют смысл для человека, что никогда не относится к двоичным данным.
Марк Рэнсом
1
Вы правы, struct.pack также подходит, если вы собираетесь записывать данные со значениями выше 255, поскольку ни bytearray, ни chr не могут обрабатывать большие целочисленные значения.
Perkins
1
@MarkRansom: Что ж, это определенно хорошее решение более общей проблемы: «У меня есть список целых чисел произвольного, но фиксированного размера, как я могу записать их в двоичный файл?» и я вижу, как люди ищут этот вопрос и находят этот…
abarnert
2
struct.pack - лучший ответ; он намного более гибкий, чем просто создание массива байтов.
Сет,
12

Чтобы преобразовать целые числа <256 в двоичные, используйте chrфункцию. Итак, вы собираетесь сделать следующее.

newFileBytes=[123,3,255,0,100]
newfile=open(path,'wb')
newfile.write((''.join(chr(i) for i in newFileBytes)).encode('charmap'))
Perkins
источник
1
Вы должны иметь в виду <128. Как сообщает python3: UnicodeEncodeError: кодек ascii не может кодировать символ '\ x89' в позиции 0: порядковый номер не в диапазоне (128)
elig
2
Нет, я имею в виду <256, но кодировка должна быть, charmapа не ascii, и работает как на python2, так и на python3. asciiКодирование работает только в python2.
Perkins
10

Начиная с Python 3.2+, вы также можете сделать это с помощью to_bytesсобственного метода int:

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

Т.е. каждый отдельный вызов to_bytesв этом случае создает строку длиной 1 с ее символами, расположенными в обратном порядке (что тривиально для строк длиной 1), что представляет собой целое значение byte. Вы также можете сократить последние две строки в одну:

newFile.write(''.join([byte.to_bytes(1, byteorder='big') for byte in newFileBytes]))
CrepeGoat
источник
8

Вы можете использовать следующий пример кода с синтаксисом Python 3:

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

Вот однострочник оболочки:

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

Используйте рассол, например: импортный рассол

Ваш код будет выглядеть так:

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

Чтобы прочитать данные обратно, используйте метод pickle.load

Раймонд Мламбо
источник
3
При этом не создается двоичный файл длиной 5 байтов, где единственным содержимым является 120, 3, 255, 0, 100. В закрытой системе это может быть приемлемо.
Парвус