2016-03-18 15 views
5

Ho creato la seguente funzione per estrarre i dati da un file. Funziona bene, ma diventa molto lento per i file più grandi.Ottieni dati da un file senza itarlo attraverso più volte

def get_data(file, indexes, data_start, sieve_first = is_float): 
    file_list = list(file) 
    for i in indexes: 
     d_line = i+data_start 
     for line in file_list[d_line:]: 
      if sieve_first(line.strip().split(',')[0]): 
       yield file_list[d_line].strip() 
       d_line += 1 
      else: 
       break 

def is_float(f): 
    try: 
     float(str(f)) 
    except: 
     return False 
    else: 
     return True 

with open('my_data') as f: 
    data = get_data(f, index_list, 3) 

Il file potrebbe assomigliare a questo (i numeri di riga aggiunte per chiarezza):

line 1234567: # <-- INDEX 
line 1234568: # +1 
line 1234569: # +2 
line 1234570:  8, 17.0, 23, 6487.6 
line 1234571:  8, 17.0, 23, 6487.6 
line 1234572:  8, 17.0, 23, 6487.6 
line 1234572: 
line 1234572: 
line 1234572: 

Con l'esempio precedente, le linee 1.234.570 1.234.572 attraverso verranno ceduti.

Poiché i miei file sono grandi, ci sono un paio di cose che non mi piacciono della mia funzione.

  1. Il primo è che legge l'intero file in memoria; Lo faccio in modo da poter utilizzare l'indicizzazione di riga per analizzare i dati.
  2. Il secondo è che le stesse righe nel file vengono ripetute più volte - questo diventa molto costoso per un file di grandi dimensioni.

Mi sono messo a trafficare cercando di utilizzare gli iteratori per passare il file una volta sola, ma non sono riuscito a decifrarlo. Eventuali suggerimenti?

+0

Basta fare 'per line in file' invece di' list (file) ' –

+0

@ cricket_007 grazie, ma se' file' non supporta l'indicizzazione, il resto della funzione non funzionerebbe. –

+0

Se il file non supporta l'indicizzazione, allora come stai 'file [d_line:]'? –

risposta

2

Se si desidera solo una piccola parte del file, utilizzare itertools.islice. Questa funzione non memorizzerà alcun dato ma i dati desiderati in memoria.

Ecco un esempio:

from itertools import islice 

def yield_specific_lines_from_file(filename, start, stop): 
    with open(filename, 'rb') as ifile: 
     for line in islice(ifile, start, stop): 
      yield line 

lines = list(yield_specific_lines_from_file('test.txt', 10, 20)) 

Se si utilizza Python 3.3 o più recente, si può anche semplificare questo utilizzando l'yield from dichiarazione:

from itertools import islice 

def yield_specific_lines_from_file(filename, start, stop): 
    with open(filename, 'rb') as ifile: 
     yield from islice(ifile, start, stop) 

lines = list(yield_specific_lines_from_file('test.txt', 10, 20)) 

Questo non memorizzare nella cache le linee che avete già letto dal file però. Se lo desideri, ti suggerisco di memorizzare tutte le righe di lettura in un dizionario con il numero di riga come chiave e di estrarre i dati dal file solo se necessario.

+1

sembra' islice' è quello che mi serviva. devono ancora scorrere più volte, ma in questo modo è molto meno costoso. Grazie. –

1

Fuori campo sinistro un po '. Ma se hai il controllo sui tuoi file, puoi spostare i dati in un db sqlite3.

Dai un'occhiata anche a mmap e linecache. Immagino che questi ultimi due siano solo involucri attorno ai file di accesso casuale. Ad esempio, è possibile eseguire personalmente il rollover eseguendo la scansione dei file una volta, quindi creando una tabella di ricerca index-> ​​offset e utilizzando seek.

Alcuni di questi approcci presuppongono che tu abbia il controllo dei file che stai leggendo?

Dipende anche dal fatto che si legge molto e si scrive di rado (in tal caso la creazione di un indice non è una cattiva idea).

+0

leggendo molto e scrivendo quasi mai, ma forse di tanto in tanto. grandi idee - grazie! –