Apa cara idiomatis untuk mengulangi file biner?

Dengan file teks, saya bisa menulis ini:

with open(path, 'r') as file:
    for line in file:
        # handle the line

Ini setara dengan ini:

with open(path, 'r') as file:
    for line in iter(file.readline, ''):
        # handle the line

Ungkapan ini didokumentasikan dalam PEP 234 tetapi saya gagal menemukan ungkapan serupa idiom untuk file biner.

Dengan file biner, saya bisa menulis ini:

with open(path, 'rb') as file:
    while True:
        chunk = file.read(1024 * 64)
        if not chunk:
            break
        # handle the chunk

Saya telah mencoba idiom yang sama dengan file teks:

def make_read(file, size):
    def read():
        return file.read(size)
    return read

with open(path, 'rb') as file:
    for chunk in iter(make_read(file, 1024 * 64), b''):
        # handle the chunk

Apakah ini cara idiomatis untuk mengulangi file biner dengan Python?


person dawg    schedule 30.12.2010    source sumber


Jawaban (4)


Saya tidak tahu cara bawaan untuk melakukan ini, tetapi fungsi wrapper cukup mudah untuk ditulis:

def read_in_chunks(infile, chunk_size=1024*64):
    while True:
        chunk = infile.read(chunk_size)
        if chunk:
            yield chunk
        else:
            # The chunk was empty, which means we're at the end
            # of the file
            return

Kemudian pada prompt interaktif:

>>> from chunks import read_in_chunks
>>> infile = open('quicklisp.lisp')
>>> for chunk in read_in_chunks(infile):
...     print chunk
... 
<contents of quicklisp.lisp in chunks>

Tentu saja, Anda dapat dengan mudah mengadaptasi ini untuk menggunakan blok with:

with open('quicklisp.lisp') as infile:
    for chunk in read_in_chunks(infile):
        print chunk

Dan Anda bisa menghilangkan pernyataan if seperti ini.

def read_in_chunks(infile, chunk_size=1024*64):
    chunk = infile.read(chunk_size)
    while chunk:
        yield chunk
        chunk = infile.read(chunk_size)
person Jason Baker    schedule 30.12.2010
comment
Saya berasumsi ada cara bawaan yang saya abaikan. Karena sepertinya tidak ada cara bawaan, ini mudah dibaca dan langsung. Terima kasih! - person dawg; 31.12.2010

Mencoba:

>>> with open('dups.txt','rb') as f:
...    for chunk in iter((lambda:f.read(how_many_bytes_you_want_each_time)),''):
...       i+=1

iter memerlukan fungsi dengan argumen nol.

  • f.read biasa akan membaca seluruh file, karena parameter size hilang;
  • f.read(1024) berarti memanggil suatu fungsi dan meneruskan nilai kembaliannya (data yang diambil dari file) ke iter, jadi iter tidak mendapatkan fungsi sama sekali;
  • (lambda:f.read(1234)) adalah fungsi yang tidak menggunakan argumen (tidak ada antara lambda dan :) dan memanggil f.read(1234).

Ada kesetaraan antara berikut:

somefunction = (lambda:f.read(how_many_bytes_you_want_each_time))

Dan

def somefunction(): return f.read(how_many_bytes_you_want_each_time)

dan memiliki salah satu dari ini sebelum kode Anda, Anda cukup menulis: iter(somefunction, '').

Secara teknis Anda dapat melewati tanda kurung di sekitar lambda, tata bahasa python akan menerimanya.

person liori    schedule 30.12.2010
comment
Ya, trik penjaga dengan iter() sangat rapi! (Meskipun saya tidak suka lambda, jadi saya akan membuat fungsinya). - person Lennart Regebro; 31.12.2010
comment
Itu bekerja! Terima kasih. Sulit untuk menghilangkan idiom lama (Perl) dan mempelajari idiom baru sambil tetap produktif. - person dawg; 31.12.2010
comment
Ini berhasil...tapi menurut saya agak sulit untuk dibaca. - person Jason Baker; 31.12.2010
comment
@Lennart Regebro, @Jason Baker: Saya berasumsi OP ingin mengetahui mengapa panggilan iter-nya tidak berhasil. Menulis sebuah iterator adalah apa yang mungkin juga akan saya lakukan dalam kasus ini, kecuali bekerja dalam prompt interaktif. - person liori; 31.12.2010
comment
@liori - Poin bagus. Saya melewatkan fakta bahwa OP mengulangi f.read. - person Jason Baker; 31.12.2010
comment
functools.partial(f.read, numBytes) juga bisa digunakan sebagai pengganti lambda - person Jochen Ritzel; 31.12.2010
comment
Penjaga harus berupa bytestring kosong, b''. String literal adalah objek Unicode di Python 3 atau dengan from __future__ import unicode_literals di Python 2. - person George V. Reilly; 01.12.2016

Cara Pythonic untuk membaca file biner secara berulang adalah menggunakan fungsi bawaan iter dengan dua argumen dan fungsi standar functools.partial, seperti yang dijelaskan dalam Dokumentasi perpustakaan Python:

iter(objek[, penjaga])

Mengembalikan objek iterator. Argumen pertama diinterpretasikan sangat berbeda tergantung kehadiran argumen kedua. Tanpa argumen kedua, objek harus berupa objek kumpulan yang mendukung protokol iterasi (metode __iter__()), atau harus mendukung protokol urutan (metode __getitem__() dengan argumen bilangan bulat dimulai dari 0). Jika tidak mendukung salah satu protokol tersebut, TypeError dimunculkan. Jika argumen kedua, sentinel, diberikan, maka objek harus berupa objek yang dapat dipanggil. Iterator yang dibuat dalam kasus ini akan memanggil objek tanpa argumen untuk setiap panggilan ke metode __next__()-nya; jika nilai yang dikembalikan sama dengan sentinel, StopIteration akan dimunculkan, jika tidak maka nilai akan dikembalikan.

Lihat juga Jenis Iterator.

Salah satu aplikasi yang berguna dari bentuk kedua iter() adalah untuk membangun pembaca blok. Misalnya, membaca blok dengan lebar tetap dari file database biner hingga akhir file tercapai:

from functools import partial

with open('mydata.db', 'rb') as f:
    for block in iter(partial(f.read, 64), b''):
        process_block(block)
person Maggyero    schedule 04.09.2019

Hampir 10 tahun setelah pertanyaan ini dan sekarang Python 3.8 memiliki := Operator Walrus yang dijelaskan dalam PEP 572.

Untuk membaca file dalam potongan secara idiomatis dan ekspresif (dengan Python 3.8 atau lebih baru), Anda dapat melakukan:

# A loop that cannot be trivially rewritten using 2-arg iter().
while chunk := file.read(1024 * 64):
    process(chunk)
person dawg    schedule 14.04.2020
comment
saya mendapat potongan := input_file.read(1024 * 64): ^ SyntaxError: sintaks tidak valid - person user1; 10.02.2021
comment
Apakah Anda menggunakan Python 3.8+? - person dawg; 22.02.2021