2015-01-06 8 views
8

Utilizzando python, mi piacerebbe leggere su un dizionario tutte le linee di un file di testo che arrivano dopo una particolare stringa. Mi piacerebbe farlo su migliaia di file di testo.Come leggere solo le righe in un file di testo dopo una determinata stringa usando python?

sono in grado di identificare e stampare la stringa particolare ('astratto') utilizzando il seguente codice (ottenuto da this stack overflow answer):

for files in filepath: 
    with open(files, 'r') as f: 
     for line in f: 
      if 'Abstract' in line: 
       print line; 

Ma come faccio a dire a Python per iniziare a leggere le linee che vieni solo dopo la stringa?

risposta

14

solo iniziare un altro ciclo quando si raggiunge la linea che si desidera iniziare da:

for files in filepath: 
    with open(files, 'r') as f: 
     for line in f: 
      if 'Abstract' in line:     
       for line in f: # now you are at the lines you want 
        # do work 

Un file oggetto è il proprio iteratore, in modo che quando si raggiunge la linea con astratta in essa continuiamo la nostra iterazione da quella linea fino a quando non abbiamo consumato l'iteratore.

Un semplice esempio:

gen = (n for n in xrange(8)) 

for x in gen: 
    if x == 3: 
     print("starting second loop") 
     for x in gen: 
      print("In second loop",x) 
    else: 
     print("In first loop", x) 

In first loop 0 
In first loop 1 
In first loop 2 
starting second loop 
In second loop 4 
In second loop 5 
In second loop 6 
In second loop 7 

È inoltre possibile utilizzare itertools.dropwhile di consumare le linee fino al punto che si desidera.

from itertools import dropwhile 

for files in filepath: 
    with open(files, 'r') as f: 
     dropped = dropwhile(lambda _line: "Abstract" not in _line, f) 
     next(dropped,"") 
     for line in dropped: 
       print(line) 
+0

Funziona, ma è un po 'strano, non credi? e chiunque non capisca come funzionano i generatori si gratterà la testa da * perché * produce un output corretto. – Kroltan

+0

@Kroltan, beh presumo che le persone che guardano a python sappiano come funziona il codice Python. Questo è python piuttosto semplice –

+0

Beh, ma non sarei così sicuro che l'OP ne fosse a conoscenza. – Kroltan

7

Utilizzare un valore booleano di ignorare le linee fino a quel momento:

found_abstract = False 
for files in filepath: 
    with open(files, 'r') as f: 
     for line in f: 
      if 'Abstract' in line: 
       found_abstract = True 
      if found_abstract: 
       #do whatever you want 
+0

Questa soluzione ha anche risposto alla domanda che ho posto. –

4

solo per chiarire, il codice già "legge" tutte le linee. Per iniziare a "prestare attenzione" alle linee dopo un certo punto, puoi semplicemente impostare un flag booleano per indicare se le linee dovrebbero o meno essere ignorate e controllarle su ogni riga.

pay_attention = False 
for line in f: 
    if pay_attention: 
     print line 
    else: # We haven't found our trigger yet; see if it's in this line 
     if 'Abstract' in line: 
      pay_attention = True 

Se non ti dispiace un po 'più risistemare del codice, è possibile utilizzare anche due loop parziali invece: un ciclo che termina una volta che hai trovato la tua frase di innesco ('Abstract'), e uno che legge tutte le righe seguenti. Questo approccio è un po 'più pulito (e un po' più veloce).

for skippable_line in f: # First skim over all lines until we find 'Abstract'. 
    if 'Abstract' in skippable_line: 
     break 
for line in f: # The file's iterator starts up again right where we left it. 
    print line 

Il motivo per cui funziona è che l'oggetto file restituito da open si comporta come un generator, piuttosto che, diciamo, un elenco: produce solo i valori in cui sono richiesti. Quindi, quando si arresta il primo ciclo, il file viene lasciato con la sua posizione interna impostata all'inizio della prima riga "non letta". Ciò significa che quando inserisci il secondo ciclo, la prima riga che vedi è la prima riga dopo quella che ha attivato lo break.

+0

Anche questa soluzione ha risposto alla domanda che ho posto. –

5

È possibile utilizzare itertools.dropwhile e itertools.islice qui, uno pseudo-esempio:

from itertools import dropwhile, islice 

for fname in filepaths: 
    with open(fname) as fin: 
     start_at = dropwhile(lambda L: 'Abstract' not in L.split(), fin) 
     for line in islice(start_at, 1, None): # ignore the line still with Abstract in 
      print line 
+0

Mi piace ... Sono sempre sorpreso da 'itertools'! – Kroltan

+0

Ero editing per dropwhile fino a quel momento i vostri ans appered, bello, uno – Hackaholic

1

fare un ipotesi su come il dizionario è coinvolto, mi piacerebbe scrivere in questo modo:

lines = dict() 
for filename in filepath: 
    with open(filename, 'r') as f: 
     for line in f: 
      if 'Abstract' in line: 
       break 
     lines[filename] = tuple(f) 

Quindi, per ogni file, il dizionario contiene una tupla di linee.

Questo funziona perché il loop legge fino alla linea identificata, compresa la linea che si identifica, lasciando le righe rimanenti nel file pronto per essere letto da f.

3

Per me, il seguente codice è più facile da capire.

with open(file_name, 'r') as f: 
    while not 'Abstract' in next(f): 
     pass 
    for line in f: 
     #line will be now the next line after the one that contains 'Abstract' 
+1

sto ottenendo AttributeError: oggetto '_io.TextIOWrapper' non ha alcun attributo 'prossimo' – yehudahs

+0

Hoy sono probabilmente utilizzando Python 3.0. Prova 'prossimo (f)' invece di 'f.next()' e fatemi sapere se ha funzionato. – eguaio

+0

funziona ... grazie !! – yehudahs