Python làm thế nào để ghi vào một tệp nhị phân?


128

Tôi có một danh sách các byte dưới dạng số nguyên, giống như

[120, 3, 255, 0, 100]

Làm cách nào để ghi danh sách này vào tệp dưới dạng tệp nhị phân?

Điều này sẽ hoạt động?

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

60
Bạn hỏi "Điều này sẽ hoạt động?". Bạn đã thử chưa?
StephenTG

1
Nên TypeError: argument 1 must be string or buffer, not list.
anatoly techtonik

Câu trả lời:


128

Đây chính xác bytearraylà những gì dành cho:

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

Nếu bạn đang sử dụng Python 3.x, bạn có thể sử dụng bytesthay thế (và có thể nên dùng, vì nó báo hiệu ý định của bạn tốt hơn). Nhưng trong Python 2.x, điều đó sẽ không hoạt động, bởi vì bytesnó chỉ là một bí danh cho str. Như thường lệ, hiển thị bằng trình thông dịch tương tác dễ hơn giải thích bằng văn bản, vì vậy hãy để tôi làm điều đó.

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
Sử dụng tốt các loại nội trang. Chỉ cần lưu ý rằng bytearray đã được thêm vào 2.6, nếu bạn muốn hỗ trợ các hệ thống kế thừa thì nên tránh nó.
Perkins

7
@Perkins: Chắc chắn rồi, và bạn nên tránh các biểu thức của trình tạo nếu bạn cần làm việc trên 2.3, hãy cẩn thận với cả hai str.encodestruct.packnếu bạn cần làm việc trên 2.2. Nhưng 2,6 đã được 5 năm nay; cả ba Ubuntu LTS vẫn được hỗ trợ, cả ba phiên bản OS X được hỗ trợ, phiên bản chính trước đó của CentOS / RHEL, v.v., tất cả đều được tích hợp sẵn. Nếu bạn cần hỗ trợ 2.5 hoặc 2.1 hoặc 1.6 hoặc bất cứ điều gì, bạn có thể biết…
abarnert

4
Với Python 2 trên Windows, tôi thấy rằng việc viết a bytearrayvẫn chuyển đổi \nthành \r\n, khiến nó không đạt yêu cầu đối với dữ liệu nhị phân, nếu cờ "b" không được chuyển khi mở tệp.
feersum

6
@feersum: Tất nhiên; đó là ý nghĩa của chế độ nhị phân so với văn bản trong 2.x. Không quan trọng loại byte của bạn đến từ đâu. (Trong 3.x, tất nhiên, nhị phân có nghĩa là chế độ văn bản vs mà bạn viết byte vs unicode, và các \r\ntính năng là một phần của các tùy chọn dòng mới phổ quát cho văn bản.)
abarnert

Tôi không chắc bytearray () là một lựa chọn tốt để ghi tệp. Bạn sẽ cần giới hạn kích thước ở những phần có thể quản lý được. Nếu không, khi kích thước tệp của bạn quá cao, bạn sẽ hết bộ nhớ.
mckenzm

31

Sử dụng struct.packđể chuyển đổi các giá trị nguyên thành byte nhị phân, sau đó ghi các byte. Ví dụ

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

Tuy nhiên, tôi sẽ không bao giờ cung cấp cho một tệp nhị phân một .txtphần mở rộng.

Lợi ích của phương pháp này là nó cũng hoạt động với các kiểu khác, ví dụ: nếu bất kỳ giá trị nào lớn hơn 255, bạn có thể sử dụng '5i'cho định dạng thay thế để nhận số nguyên 32 bit đầy đủ.


.txt là tốt nếu bạn có một số cách để biết rằng dữ liệu bạn đang ghi tất cả đều nằm trong phạm vi ascii có thể in được. Tuy nhiên, bạn nói đúng, tôi nghĩ trong trường hợp này, vì dữ liệu ví dụ bao gồm các ký tự không in được.
Perkins

@Perkins Tôi đã không đưa ra giả định rằng các giá trị thậm chí sẽ nhỏ hơn 256 ít hơn nhiều trong phạm vi ASCII. Ngay cả khi có, các tệp .txt nên được dành riêng cho những tệp có ý nghĩa đối với con người mà không bao giờ áp dụng cho dữ liệu nhị phân.
Mark Ransom

1
Bạn nói đúng, struct.pack cũng là cách tốt nhất nếu bạn định ghi dữ liệu có giá trị trên 255, vì cả bytearray và chr đều không thể xử lý các giá trị số nguyên lớn hơn.
Perkins

1
@MarkRansom: Chà, đây chắc chắn vẫn là một giải pháp tốt cho vấn đề tổng quát hơn là "Tôi có một danh sách các số nguyên có kích thước tùy ý nhưng cố định, làm cách nào để ghi chúng vào tệp nhị phân?" và tôi có thể nhìn thấy những người tìm kiếm câu hỏi đó và tìm ra một cái này ...
abarnert

1
struct.pack là câu trả lời tốt hơn; nó linh hoạt hơn nhiều so với việc chỉ tạo một bytearray.
Seth

12

Để chuyển đổi từ số nguyên <256 sang nhị phân, hãy sử dụng chrhàm. Vì vậy, bạn đang xem xét làm như sau.

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

1
Bạn phải có nghĩa <128. Như python3 phàn nàn: UnicodeEncodeError: 'ascii' codec có thể không mã hóa kí tự '\ x89' ở vị trí 0: thứ tự không trong phạm vi (128)
elig

2
Không, ý tôi là <256, nhưng mã hóa phải charmapđúng hơn asciivà hoạt động trong python2 cũng như python3. Các asciimã hóa chỉ hoạt động trong python2.
Perkins

9

Kể từ Python 3.2+, bạn cũng có thể thực hiện điều này bằng cách sử dụng to_bytesphương thức int gốc:

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

Tức là, mỗi lệnh gọi đến to_bytestrong trường hợp này sẽ tạo ra một chuỗi có độ dài 1, với các ký tự của nó được sắp xếp theo thứ tự big-endian (nhỏ hơn đối với chuỗi độ dài 1), đại diện cho giá trị số nguyên byte. Bạn cũng có thể rút ngắn hai dòng cuối cùng thành một dòng:

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

8

Bạn có thể sử dụng ví dụ mã sau bằng cú pháp Python 3:

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

Đây là 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

Sử dụng dưa chua, như thế này: nhập dưa chua

Mã của bạn sẽ giống như sau:

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

Để đọc lại dữ liệu, hãy sử dụng phương thức pickle.load


3
Điều này không tạo ra tệp nhị phân có độ dài 5 byte, trong đó nội dung duy nhất là 120, 3, 255, 0, 100. Tuy nhiên, trong một hệ thống đóng, điều này có thể được chấp nhận.
parvus
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.