วิธีสำนวนในการวนซ้ำไฟล์ไบนารีคืออะไร?

ด้วยไฟล์ข้อความ ฉันสามารถเขียนสิ่งนี้ได้:

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

นี่เทียบเท่ากับสิ่งนี้:

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

สำนวนนี้ได้รับการบันทึกไว้ใน PEP 234 แต่ฉันไม่พบคำที่คล้ายกัน สำนวนสำหรับไฟล์ไบนารี

ด้วยไฟล์ไบนารี่ ฉันสามารถเขียนสิ่งนี้ได้:

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

ฉันได้ลองใช้สำนวนเดียวกันกับไฟล์ข้อความ:

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

มันเป็นวิธีที่ใช้สำนวนในการวนซ้ำไฟล์ไบนารี่ใน Python หรือไม่?


person dawg    schedule 30.12.2010    source แหล่งที่มา


คำตอบ (4)


ฉันไม่รู้วิธีในการทำเช่นนี้ แต่ฟังก์ชัน wrapper นั้นง่ายพอที่จะเขียน:

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

จากนั้นที่พร้อมท์โต้ตอบ:

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

แน่นอนคุณสามารถปรับใช้สิ่งนี้เพื่อใช้ a with block ได้อย่างง่ายดาย:

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

และคุณสามารถกำจัดคำสั่ง if เช่นนี้ได้

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
ฉันคิดว่ามีบางอย่างในตัวที่ฉันมองข้ามไป เนื่องจากดูเหมือนว่าจะไม่มีวิธีการติดตั้งในตัว จึงอ่านง่ายและตรงไปตรงมา ขอบคุณ! - person dawg; 31.12.2010

พยายาม:

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

iter ต้องการฟังก์ชันที่ไม่มีอาร์กิวเมนต์

  • f.read ธรรมดาจะอ่านทั้งไฟล์ เนื่องจากพารามิเตอร์ size ที่ขาดหายไป;
  • f.read(1024) หมายถึงการเรียกใช้ฟังก์ชันและส่งค่าส่งคืน (ข้อมูลที่โหลดจากไฟล์) ไปที่ iter ดังนั้น iter จึงไม่ได้รับฟังก์ชันเลย
  • (lambda:f.read(1234)) เป็นฟังก์ชันที่รับอาร์กิวเมนต์เป็นศูนย์ (ไม่มีอะไรระหว่าง lambda ถึง :) และเรียก f.read(1234)

มีความเท่าเทียมกันระหว่างสิ่งต่อไปนี้:

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

และ

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

และมีอย่างใดอย่างหนึ่งอยู่ข้างหน้าโค้ดของคุณ คุณก็สามารถเขียนได้: iter(somefunction, '')

ในทางเทคนิคแล้ว คุณสามารถข้ามวงเล็บรอบแลมบ์ดาได้ ไวยากรณ์ของไพธอนจะยอมรับสิ่งนั้น

person liori    schedule 30.12.2010
comment
ใช่ เคล็ดลับของแมวมองด้วย iter() นั้นเรียบร้อยจริงๆ! (แม้ว่าฉันจะไม่ชอบแลมบ์ดา แต่ฉันก็จะสร้างฟังก์ชั่นขึ้นมา) - person Lennart Regebro; 31.12.2010
comment
นั่นได้ผล! ขอบคุณ. เป็นเรื่องยากที่จะสูญเสียสำนวนเก่า (Perl) และเรียนรู้สำนวนใหม่ในขณะที่ยังคงมีประสิทธิผลพอสมควร - person dawg; 31.12.2010
comment
ใช้งานได้...แต่ในความคิดของฉันอ่านยากนิดหน่อย - person Jason Baker; 31.12.2010
comment
@Lennart Regebro, @Jason Baker: ฉันคิดว่า OP ต้องการเรียนรู้ว่าทำไมการโทร iter ของเขาถึงไม่ทำงาน การเขียนตัววนซ้ำคือสิ่งที่ฉันอาจจะทำในกรณีนี้ เว้นแต่ว่าจะทำงานในพร้อมท์แบบโต้ตอบ - person liori; 31.12.2010
comment
@liori - จุดดี ฉันพลาดความจริงที่ว่า OP กำลังวนซ้ำมากกว่า f.read - person Jason Baker; 31.12.2010
comment
functools.partial(f.read, numBytes) ควรทำงานแทน lambda ด้วยเช่นกัน - person Jochen Ritzel; 31.12.2010
comment
Sentinel ควรเป็นสตริงไบต์ว่าง b'' ตัวอักษรสตริงเป็นวัตถุ Unicode ใน Python 3 หรือด้วย from __future__ import unicode_literals ใน Python 2 - person George V. Reilly; 01.12.2016

วิธี Pythonic ในการอ่านไฟล์ไบนารีซ้ำๆ คือการใช้ฟังก์ชันในตัว iter ที่มีสองอาร์กิวเมนต์ และฟังก์ชันมาตรฐาน functools.partial ตามที่อธิบายไว้ใน เอกสารประกอบของไลบรารี Python:

iter(วัตถุ[, แมวมอง])

กลับวัตถุตัววนซ้ำ อาร์กิวเมนต์แรกถูกตีความแตกต่างกันมากขึ้นอยู่กับการมีอยู่ของอาร์กิวเมนต์ที่สอง หากไม่มีอาร์กิวเมนต์ที่สอง วัตถุ จะต้องเป็นวัตถุคอลเลกชันที่รองรับโปรโตคอลการวนซ้ำ (วิธี __iter__()) หรือต้องสนับสนุนโปรโตคอลลำดับ (วิธี __getitem__() ที่มีอาร์กิวเมนต์จำนวนเต็มเริ่มต้นที่ 0) หากไม่รองรับโปรโตคอลใดโปรโตคอลหนึ่ง TypeError จะถูกยกขึ้น ถ้าอาร์กิวเมนต์ที่สอง sentinel ถูกกำหนดไว้ object จะต้องเป็นอ็อบเจ็กต์ที่สามารถเรียกได้ ตัววนซ้ำที่สร้างขึ้นในกรณีนี้จะเรียก object โดยไม่มีอาร์กิวเมนต์สำหรับการเรียกเมธอด __next__() แต่ละครั้ง หากค่าที่ส่งคืนเท่ากับ sentinel ค่า StopIteration จะถูกยกขึ้น ไม่เช่นนั้นค่าจะถูกส่งคืน

ดูเพิ่มเติมที่ ประเภทตัววนซ้ำ

แอปพลิเคชันที่มีประโยชน์อย่างหนึ่งของรูปแบบที่สองของ iter() คือการสร้างตัวอ่านแบบบล็อก ตัวอย่างเช่น การอ่านบล็อกที่มีความกว้างคงที่จากไฟล์ฐานข้อมูลไบนารี่จนกระทั่งถึงจุดสิ้นสุดของไฟล์:

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

เกือบ 10 ปีหลังจากคำถามนี้ และตอนนี้ Python 3.8 มี := ตัวดำเนินการ Walrus ที่อธิบายไว้ใน PEP 572.

หากต้องการอ่านไฟล์เป็นชิ้น ๆ ตามสำนวนและการแสดงออก (ด้วย Python 3.8 หรือใหม่กว่า) คุณสามารถทำได้:

# 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
ฉันได้รับ while chunk := input_file.read(1024 * 64): ^ SyntaxError: invalid syntax - person user1; 10.02.2021
comment
คุณใช้ Python 3.8+ หรือไม่? - person dawg; 22.02.2021