Dengan Python, bagaimana cara membaca dalam file biner dan mengulangi setiap byte file itu?
Python 2.4 dan Sebelumnya
f = open("myfile", "rb")
try:
byte = f.read(1)
while byte != "":
# Do stuff with byte.
byte = f.read(1)
finally:
f.close()
Python 2.5-2.7
with open("myfile", "rb") as f:
byte = f.read(1)
while byte != "":
# Do stuff with byte.
byte = f.read(1)
Perhatikan bahwa pernyataan with tidak tersedia dalam versi Python di bawah 2.5. Untuk menggunakannya di v 2.5 Anda harus mengimpornya:
from __future__ import with_statement
Dalam 2.6 ini tidak diperlukan.
Python 3
Dalam Python 3, ini sedikit berbeda. Kita tidak akan lagi mendapatkan karakter mentah dari stream dalam mode byte tetapi objek byte, jadi kita perlu mengubah kondisinya:
with open("myfile", "rb") as f:
byte = f.read(1)
while byte != b"":
# Do stuff with byte.
byte = f.read(1)
Atau seperti yang dikatakan benhoyt, lewatkan yang tidak sama dan manfaatkan fakta yang b""
dinilai salah. Ini membuat kode tersebut kompatibel antara 2.6 dan 3.x tanpa perubahan apa pun. Ini juga akan menyelamatkan Anda dari mengubah kondisi jika Anda beralih dari mode byte ke teks atau sebaliknya.
with open("myfile", "rb") as f:
byte = f.read(1)
while byte:
# Do stuff with byte.
byte = f.read(1)
python 3.8
Mulai sekarang terima kasih kepada: = operator kode di atas dapat ditulis dengan cara yang lebih singkat.
with open("myfile", "rb") as f:
while (byte := f.read(1)):
# Do stuff with byte.
Generator ini menghasilkan byte dari file, membaca file dalam potongan:
Lihat dokumentasi Python untuk informasi tentang iterator dan generator .
sumber
8192 Byte = 8 kB
(sebenarnya ituKiB
tapi itu tidak dikenal secara umum). Nilainya "benar-benar" acak tetapi 8 kB tampaknya merupakan nilai yang sesuai: tidak terlalu banyak memori yang terbuang dan masih tidak ada "terlalu banyak" operasi baca seperti dalam jawaban yang diterima oleh Skurmedel ...for b in chunk:
loop paling dalam denganyield from chunk
. Bentukyield
ini ditambahkan dalam Python 3.3 (lihat Yield Expressions ).Jika file tidak terlalu besar yang menahannya dalam memori adalah masalah:
di mana process_byte mewakili beberapa operasi yang ingin Anda lakukan pada byte yang lewat.
Jika Anda ingin memproses chunk sekaligus:
The
with
pernyataan tersedia dalam Python 2,5 dan lebih besar.sumber
Untuk membaca file - satu byte pada satu waktu (mengabaikan buffering) - Anda bisa menggunakan fungsi built-in dua argumen
iter(callable, sentinel)
:Itu panggilan
file.read(1)
sampai tidak menghasilkan apa-apab''
(bytestring kosong). Memori tidak tumbuh tanpa batas untuk file besar. Anda dapat beralihbuffering=0
keopen()
, untuk menonaktifkan buffering - itu menjamin bahwa hanya satu byte dibaca per iterasi (lambat).with
-Statement menutup file secara otomatis - termasuk case ketika kode di bawahnya memunculkan exception.Meskipun ada penyangga internal secara default, masih tidak efisien untuk memproses satu byte pada suatu waktu. Misalnya, inilah
blackhole.py
utilitas yang memakan semua yang diberikan:Contoh:
Ini memproses ~ 1,5 GB / s saat
chunksize == 32768
di komputer saya dan hanya ~ 7,5 MB / s saatchunksize == 1
. Artinya, 200 kali lebih lambat untuk membaca satu byte pada suatu waktu. Pertimbangkan jika Anda dapat menulis ulang pemrosesan Anda untuk menggunakan lebih dari satu byte pada suatu waktu dan jika Anda membutuhkan kinerja.mmap
memungkinkan Anda untuk memperlakukan file sebagaibytearray
objek file dan secara bersamaan. Ini dapat berfungsi sebagai alternatif untuk memuat seluruh file dalam memori jika Anda perlu mengakses kedua antarmuka. Secara khusus, Anda dapat mengulangi satu byte pada suatu waktu melalui file yang dipetakan dengan memori hanya menggunakanfor
-loop polos :mmap
mendukung notasi slice. Misalnya,mm[i:i+len]
mengembalikanlen
byte dari file mulai dari posisii
. Protokol manajer konteks tidak didukung sebelum Python 3.2; Anda perlu meneleponmm.close()
secara eksplisit dalam hal ini. Iterasi setiap byte menggunakanmmap
memori lebih banyak daripadafile.read(1)
, tetapimmap
urutan besarnya lebih cepat.sumber
numpy
array (byte) memori-dipetakan setara .numpy.memmap()
dan Anda bisa mendapatkan data satu byte setiap kali (ctypes.data). Anda bisa menganggap array numpy hanya sedikit lebih dari gumpalan di memori + metadata.Baru dalam Python 3.5 adalah
pathlib
modul, yang memiliki metode kenyamanan khusus untuk membaca dalam file sebagai byte, memungkinkan kita untuk beralih pada byte. Saya menganggap ini jawaban yang layak (jika cepat dan kotor):Menarik bahwa ini adalah satu-satunya jawaban untuk disebutkan
pathlib
.Dalam Python 2, Anda mungkin akan melakukan ini (seperti yang disarankan Vinay Sajip):
Jika file mungkin terlalu besar untuk di-in-memory, Anda akan memotongnya, secara idiomatis, menggunakan
iter
fungsi dengancallable, sentinel
tanda tangan - versi Python 2:(Beberapa jawaban lain menyebutkan ini, tetapi sedikit yang menawarkan ukuran baca yang masuk akal.)
Praktik terbaik untuk file besar atau buffered / reading interaktif
Mari kita membuat fungsi untuk melakukan ini, termasuk penggunaan idiomatik dari perpustakaan standar untuk Python 3.5+:
Perhatikan bahwa kami menggunakan
file.read1
.file.read
blok sampai mendapat semua byte yang diminta atauEOF
.file.read1
memungkinkan kita untuk menghindari pemblokiran, dan itu dapat kembali lebih cepat karena ini. Tidak ada jawaban lain yang menyebutkan ini juga.Demonstrasi penggunaan praktik terbaik:
Mari kita membuat file dengan megabyte (sebenarnya mebibyte) dari data pseudorandom:
Sekarang mari kita beralih dan mewujudkannya dalam memori:
Kami dapat memeriksa bagian mana pun dari data, misalnya, 100 byte terakhir dan 100 byte pertama:
Jangan beralih berdasarkan baris untuk file biner
Jangan lakukan hal berikut - ini menarik sepotong ukuran acak hingga mencapai karakter baris baru - terlalu lambat ketika potongan terlalu kecil, dan mungkin terlalu besar juga:
Di atas hanya baik untuk file teks yang dapat dibaca manusia secara semantik (seperti teks biasa, kode, markup, penurunan harga, dll ... pada dasarnya apa saja yang ascii, utf, latin, dll ... disandikan) yang harus Anda buka tanpa
'b'
bendera.sumber
path = Path(path), with path.open('rb') as file:
daripada menggunakan fungsi terbuka built-in saja? Mereka berdua melakukan hal yang sama benar?Path
objek karena ini adalah cara baru yang sangat nyaman untuk menangani jalur. Alih-alih meneruskan sebuah string ke fungsi "benar" yang dipilih dengan cermat, kita bisa memanggil metode pada objek path, yang pada dasarnya berisi sebagian besar fungsi penting yang Anda inginkan dengan apa yang secara semantik adalah string path. Dengan IDE yang dapat menginspeksi, kita juga dapat dengan mudah mendapatkan pelengkapan otomatis. Kita bisa melakukan hal yang sama denganopen
builtin, tetapi ada banyak sisi baiknya ketika menulis program agar programmer menggunakanPath
objek.file_byte_iterator
jauh lebih cepat daripada semua metode yang saya coba di halaman ini. Kudos untuk Anda!Untuk meringkas semua poin cemerlang chrispy, Skurmedel, Ben Hoyt dan Peter Hansen, ini akan menjadi solusi optimal untuk memproses file biner satu byte pada satu waktu:
Untuk versi python 2.6 dan di atasnya, karena:
Atau gunakan solusi JF Sebastians untuk meningkatkan kecepatan
Atau jika Anda menginginkannya sebagai fungsi generator seperti yang ditunjukkan oleh codeape:
sumber
Python 3, baca semua file sekaligus:
Anda dapat mengulangi apa pun yang Anda inginkan menggunakan
data
variabel.sumber
Setelah mencoba semua hal di atas dan menggunakan jawaban dari @Aaron Hall, saya mendapatkan kesalahan memori untuk file ~ 90 Mb pada komputer yang menjalankan Window 10, 8 Gb RAM dan Python 3.5 32-bit. Saya direkomendasikan oleh seorang kolega untuk menggunakan
numpy
dan itu bekerja dengan baik.Sejauh ini, yang tercepat untuk membaca seluruh file biner (yang telah saya uji) adalah:
Referensi
Banyak orang lebih cepat daripada metode lain sejauh ini. Semoga ini bisa membantu seseorang!
sumber
numpy
, maka mungkin bermanfaat.Jika Anda memiliki banyak data biner untuk dibaca, Anda mungkin ingin mempertimbangkan modul struct . Ini didokumentasikan sebagai konversi "antara tipe C dan Python", tetapi tentu saja, byte adalah byte, dan apakah itu dibuat sebagai tipe C tidak masalah. Misalnya, jika data biner Anda berisi dua bilangan bulat 2-byte dan satu bilangan bulat 4-byte, Anda dapat membacanya sebagai berikut (contoh diambil dari
struct
dokumentasi):Anda mungkin menemukan ini lebih nyaman, lebih cepat, atau keduanya, daripada secara eksplisit mengulang konten file.
sumber
Posting ini sendiri bukan jawaban langsung untuk pertanyaan itu. Sebaliknya, itu adalah patokan yang dapat diperluas yang digerakkan oleh data yang dapat digunakan untuk membandingkan banyak jawaban (dan variasi penggunaan fitur baru yang ditambahkan di versi Python yang lebih modern dan lebih baru) yang telah diposting ke pertanyaan ini - dan karenanya harus membantu dalam menentukan mana yang memiliki kinerja terbaik.
Dalam beberapa kasus, saya telah memodifikasi kode dalam jawaban yang direferensikan untuk membuatnya kompatibel dengan kerangka acuan.
Pertama, berikut adalah hasil untuk versi Python 2 & 3 terbaru:
Saya juga menjalankannya dengan file tes 10 MiB yang jauh lebih besar (yang memakan waktu hampir satu jam untuk menjalankan) dan mendapatkan hasil kinerja yang sebanding dengan yang ditunjukkan di atas.
Berikut kode yang digunakan untuk melakukan pembandingan:
sumber
yield from chunk
bukanfor byte in chunk: yield byte
? Saya pikir saya harus memperketat jawaban saya dengan itu.yield from
.enumerate
karena iterasi harus dipahami untuk menyelesaikan - jika tidak, terakhir saya periksa - enumerate memiliki sedikit overhead dengan biaya lebih dari melakukan pembukuan untuk indeks dengan + = 1, sehingga Anda dapat melakukan alternatif pembukuan di Anda kode sendiri. Atau bahkan lulus ke deque denganmaxlen=0
.enumerate
. Terima kasih untuk umpan baliknya. Akan menambahkan pembaruan ke posting saya yang tidak memilikinya (walaupun saya pikir itu tidak banyak mengubah hasilnya). Juga akan menambahkannumpy
jawaban berbasis @Rick M.super().
alih-alihtuple.
Anda,__new__
Anda bisa menggunakannamedtuple
nama atribut alih-alih indeks.jika Anda mencari sesuatu yang cepat, berikut ini adalah metode yang saya gunakan selama bertahun-tahun:
jika Anda ingin mengulangi karakter bukan int, Anda bisa menggunakan
data = file.read()
, yang seharusnya menjadi objek byte () di py3.sumber