Python jak pisać do pliku binarnego?

129

Mam listę bajtów jako liczb całkowitych, co jest czymś w rodzaju

[120, 3, 255, 0, 100]

Jak mogę zapisać tę listę do pliku jako binarną?

Czy to zadziała?

newFileBytes = [123, 3, 255, 0, 100]
# make file
newFile = open("filename.txt", "wb")
# write to file
newFile.write(newFileBytes)
Aaron Hiniker
źródło
61
Pytasz „Czy to zadziała?”. Próbowałeś tego?
StephenTG,
1
Powinien być TypeError: argument 1 must be string or buffer, not list.
anatoly techtonik

Odpowiedzi:

130

To jest dokładnie to, do czego bytearraysłuży:

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

Jeśli korzystasz z Pythona 3.x, możesz użyć byteszamiast tego (i prawdopodobnie powinien, ponieważ lepiej sygnalizuje twoje zamiary). Ale w Pythonie 2.x to nie zadziała, ponieważ bytesjest to tylko alias dla str. Jak zwykle, pokazywanie za pomocą interaktywnego tłumacza jest łatwiejsze niż wyjaśnianie za pomocą tekstu, więc pozwól mi to zrobić.

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
źródło
1
Niezłe użycie wbudowanych typów. Zwróć uwagę, że bytearray został dodany w 2.6, jeśli chcesz obsługiwać starsze systemy, należy tego unikać.
Perkins
7
@Perkins: Jasne, i powinieneś unikać wyrażeń generatora, jeśli musisz pracować nad 2.3, bądź ostrożny z obydwoma str.encodei struct.packjeśli musisz pracować nad 2.2. Ale 2.6 jest niedostępny od 5 lat; wszystkie trzy systemy Ubuntu LTS są nadal obsługiwane, wszystkie trzy wersje OS X są obsługiwane, poprzednia główna wersja CentOS / RHEL itp., wszystkie są wbudowane. Jeśli potrzebujesz obsługiwać 2.5 lub 2.1 lub 1.6 lub cokolwiek innego, prawdopodobnie wiem…
abarnert
4
W Pythonie 2 w systemie Windows stwierdziłem, że zapis a bytearraynadal konwertuje \ndo \r\n, co czyni go niezadowalającym dla danych binarnych, jeśli flaga „b” nie jest przekazywana podczas otwierania pliku.
feersum
6
@feersum: Oczywiście; to właśnie oznacza tryb binarny kontra tryb tekstowy w wersji 2.x. Nie ma znaczenia, z jakiego typu pochodzą twoje bajty. (W 3.x, oczywiście, tryb binarny kontra tryb tekstowy oznacza, że ​​piszesz bajty, a nie Unicode, a \r\nfunkcja jest częścią uniwersalnych opcji nowych linii dla tekstu.)
abarnert
Nie jestem pewien, czy bytearray () to dobry wybór do zapisu plików. Musisz ograniczyć rozmiar do porcji, które można zarządzać. W przeciwnym razie, gdy rozmiary plików będą zbyt duże, zabraknie pamięci.
mckenzm
33

Służy struct.packdo konwersji wartości całkowitych na bajty binarne, a następnie zapisz bajty. Na przykład

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

Jednak nigdy nie nadałbym plikowi binarnemu .txtrozszerzenia.

Zaletą tej metody jest to, że działa ona również w przypadku innych typów, na przykład jeśli którakolwiek z wartości jest większa niż 255, możesz użyć '5i'zamiast tego formatu, aby uzyskać pełne 32-bitowe liczby całkowite.

Mark Okup
źródło
.txt jest w porządku, jeśli masz jakiś sposób, aby wiedzieć, że wszystkie dane, które piszesz, mieszczą się w drukowalnym zakresie ASCII. Myślę jednak, że w tym przypadku masz rację, ponieważ przykładowe dane obejmują znaki niedrukowalne.
Perkins
@Perkins Nie założyłem, że wartości będą nawet mniejsze niż 256, znacznie mniejsze w zakresie ASCII. Nawet jeśli tak jest, pliki .txt powinny być zarezerwowane dla tych, które mają sens dla człowieka, a które nigdy nie dotyczą danych binarnych.
Mark Ransom
1
Masz rację, struct.pack jest również właściwą drogą, jeśli zamierzasz zapisywać dane o wartościach powyżej 255, ponieważ ani bytearray, ani chr nie obsługują większych wartości całkowitych.
Perkins
1
@MarkRansom: Cóż, to nadal jest zdecydowanie dobre rozwiązanie bardziej ogólnego problemu: "Mam listę liczb całkowitych o dowolnym, ale stałym rozmiarze, jak mogę zapisać je do pliku binarnego?" i widzę, jak ludzie szukają tego pytania i znajdują to…
abarnert
2
struct.pack jest lepszą odpowiedzią; jest o wiele bardziej elastyczny niż zwykłe tworzenie bajtearray.
Seth
12

Aby przekonwertować liczby całkowite <256 na binarne, użyj chrfunkcji. Więc patrzysz na wykonanie następujących czynności.

newFileBytes=[123,3,255,0,100]
newfile=open(path,'wb')
newfile.write((''.join(chr(i) for i in newFileBytes)).encode('charmap'))
Perkins
źródło
1
Musisz mieć na myśli <128. Jak narzeka python3: UnicodeEncodeError: kodek „ascii” nie może zakodować znaku „\ x89” na pozycji 0:
numer
2
Nie, mam na myśli <256, ale kodowanie powinno być charmapraczej niż asciii działa zarówno w python2, jak i python3. asciiKodowania działa tylko w python2.
Perkins,
10

Od wersji Python 3.2+ możesz to również osiągnąć przy użyciu to_bytesnatywnej metody 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 znaczy, każde pojedyncze wywołanie to_bytesw tym przypadku tworzy ciąg o długości 1, z jego znakami ułożonymi w kolejności big-endian (co jest trywialne dla łańcuchów długości 1), który reprezentuje wartość całkowitą byte. Możesz także skrócić ostatnie dwie linie w jedną:

newFile.write(''.join([byte.to_bytes(1, byteorder='big') for byte in newFileBytes]))
CrepeGoat
źródło
8

Możesz użyć następującego przykładu kodu przy użyciu składni Python 3:

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

Oto jedna linijka powłoki:

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

Użyj marynaty, w ten sposób: importuj marynatę

Twój kod wyglądałby tak:

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

Aby odczytać dane z powrotem, użyj metody pickle.load

Raymond Mlambo
źródło
3
Nie tworzy to pliku binarnego o długości 5 bajtów, którego jedyna zawartość to 120, 3, 255, 0, 100. W systemie zamkniętym może to być jednak dopuszczalne.
parvus