2010-12-30 10 views
20

In Python, per un file binario, posso scrivere questo:Python file di iteratore su un file binario con le nuove idioma

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

Con un file di testo che voglio leggere riga per riga, non posso scrivere questo:

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

il che è una scorciatoia per:

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

questo idioma è documentato in PEP 234 ma ho Faile d per individuare un linguaggio simile per i file binari.

Ho provato questo:

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

Ho provato a mettere iter(f.read(buf_size),'') ma che è un errore di sintassi causa delle parentesi dopo la richiamabile in iter().

So che potrei scrivere una funzione, ma esiste l'idioma predefinito di for chunk in file: in cui posso utilizzare una dimensione del buffer rispetto a una linea orientata?

Grazie per aver sopportato il principiante Python mentre cercava di scrivere il suo primo script Python non banale e idiomatico.

risposta

20

io non conosco alcun modo incorporato per fare questo, ma una funzione wrapper è abbastanza facile da scrivere:

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 

Poi al prompt interattivo:

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

Di Naturalmente, si può facilmente adattare questo per usare un blocco con:

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

E si può eliminare l'istruzione if come questo .

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

Avevo pensato che esistesse un modo integrato che stavo semplicemente trascurando. Poiché non sembra esserci un modo integrato, questo è facile da leggere e diretto. Grazie! – dawg

30

Prova:

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

iter ha bisogno di una funzione con zero argomenti.

  • una pianura f.read avrebbe letto l'intero file, dal momento che il parametro size è mancante;
  • f.read(1024) significa chiamare una funzione e passare il suo valore di ritorno (dati caricati dal file) a iter, quindi iter non ottiene affatto una funzione;
  • (lambda:f.read(1234)) è una funzione che accetta zero argomenti (niente tra lambda e :) e chiama f.read(1234).

Non c'è equivalenza tra seguente:

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

e

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

e avere uno di questi prima il codice si può solo scrivere: iter(somefunction, '').

Tecnicamente è possibile saltare le parentesi attorno a lambda, la grammatica di Python lo accetterà.

+0

Sì, il trucco di sentinella con iter() è davvero accurato! (Anche se non mi piacciono i lambda, quindi avrei fatto una funzione). –

+0

Questo funziona! Grazie. È difficile perdere vecchi idiomi (Perl) e apprenderne di nuovi pur essendo abbastanza produttivi. – dawg

+0

Questo funziona ... ma è un po 'difficile da leggere secondo me. –

Problemi correlati