2010-12-30 25 views
20

Trong Python, cho một tập tin nhị phân, tôi có thể viết này:Python tập tin iterator qua một tập tin nhị phân với thành ngữ mới hơn

buf_size=1024*64   # this is an important size... 
with open(file, "rb") as f: 
    while True: 
     data=f.read(buf_size) 
     if not data: break 
     # deal with the data.... 

Với một file văn bản mà tôi muốn đọc line-by-line, tôi có thể viết này:

with open(file, "r") as file: 
    for line in file: 
     # deal with each line.... 

Đó là viết tắt cho:

with open(file, "r") as file: 
    for line in iter(file.readline, ""): 
     # deal with each line.... 

Thành ngữ này được ghi chép lại trong PEP 234 nhưng tôi có faile d để định vị một thành ngữ tương tự cho các tệp nhị phân.

Tôi đã thử này:

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

>>> i 
1    # 30 MB file, i==1 means read in one go... 

Tôi đã thử đưa iter(f.read(buf_size),'') nhưng đó là một lỗi cú pháp vì Parens sau callable trong iter().

Tôi biết tôi có thể viết một hàm, nhưng có cách nào với thành ngữ mặc định của for chunk in file: nơi tôi có thể sử dụng kích thước bộ đệm so với dòng được định hướng không?

Cảm ơn bạn đã đề cập đến các newbie Python đang cố gắng viết tập lệnh Python không tầm thường và thành ngữ đầu tiên của mình.

Trả lời

20

Tôi không biết về bất kỳ cách built-in để làm điều này, nhưng một chức năng wrapper là đủ dễ dàng để viết:

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 

Sau đó tại dấu nhắc tương tác:

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

Trong số tất nhiên, bạn có thể dễ dàng điều chỉnh điều này để sử dụng một khối:

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

Và bạn có thể loại bỏ câu lệnh if như thế này .

def read_in_chunks(infile, chunk_size=1024*64): 
    chunk = infile.read(chunk_size) 
    while chunk: 
     yield chunk 
     chunk = infile.read(chunk_size) 
+0

Tôi đã giả sử có một số cách dựng sẵn mà tôi vừa xem. Vì dường như không phải là cách tích hợp, điều này rất dễ đọc và dễ hiểu. Cảm ơn! – dawg

30

Hãy thử:

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

iter cần một hàm với số không đối số.

  • đồng bằng f.read sẽ đọc toàn bộ tệp vì thiếu thông số size;
  • f.read(1024) có nghĩa là gọi hàm và chuyển giá trị trả về của nó (dữ liệu được tải từ tệp) đến iter, vì vậy iter không nhận được hàm nào;
  • (lambda:f.read(1234)) là hàm không có đối số (không có gì giữa lambda:) và gọi f.read(1234).

Có tương đương giữa sau:

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

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

và có một trong những trước khi mã của bạn, bạn chỉ có thể viết: iter(somefunction, '').

Về mặt kỹ thuật, bạn có thể bỏ qua dấu ngoặc đơn quanh lambda, ngữ pháp của python sẽ chấp nhận điều đó.

+0

Vâng, thủ thuật gửi với iter() thực sự gọn gàng! (Mặc dù tôi không thích lambdas, vì vậy tôi đã có thể thực hiện một chức năng). –

+0

Điều đó có hiệu quả! Cảm ơn. Đó là khó khăn mất thành ngữ cũ (Perl) và học những cái mới trong khi vẫn đang được sản xuất hợp lý. – dawg

+0

Tác phẩm này ... nhưng hơi khó đọc theo ý kiến ​​của tôi. –

Các vấn đề liên quan