2011-09-26 15 views
12

Non sto parlando di numeri di linea specifici perché sto leggendo più file con lo stesso formato ma di lunghezza variabile.
dire che ho questo file di testo:python - Leggi il file da e verso righe di testo specifiche

Something here... 
... ... ... 
Start      #I want this block of text 
a b c d e f g 
h i j k l m n 
End      #until this line of the file 
something here... 
... ... ... 

Spero che tu sai cosa voglio dire. Stavo pensando di iterare il file, quindi di cercare usando espressioni regolari per trovare il numero di riga di "Start" e "Fine", quindi utilizzare linecache per leggere dalla riga iniziale alla riga finale. Ma come ottenere il numero di linea? quale funzione posso usare?

+0

Questa domanda è molto simile a questo http://stackoverflow.com/questions/7098530/repeatedly-extract-a-line-between-two-delimiters-in-a-text-file-python – salomonvh

risposta

21

Se si desidera semplicemente il blocco di testo tra inizio e fine, si può fare qualcosa di semplice come:

with open('test.txt') as input_data: 
    # Skips text before the beginning of the interesting block: 
    for line in input_data: 
     if line.strip() == 'Start': # Or whatever test is needed 
      break 
    # Reads text until the end of the block: 
    for line in input_data: # This keeps reading the file 
     if line.strip() == 'End': 
      break 
     print line # Line is extracted (or block_of_lines.append(line), etc.) 

In realtà, non c'è bisogno di manipolare i numeri di riga al fine di leggere i dati tra i marcatori Inizio e Fine.

La logica ("read until ...") viene ripetuta in entrambi i blocchi, ma è abbastanza chiara ed efficiente (in genere altri metodi prevedono il controllo di alcuni stati [prima del blocco/entro il blocco/fine del blocco raggiunto], che incorre in penalità di tempo).

1

Questo dovrebbe essere un inizio per voi:

started = False 
collected_lines = [] 
with open(path, "r") as fp: 
    for i, line in enumerate(fp.readlines()): 
     if line.rstrip() == "Start": 
      started = True 
      print "started at line", i # counts from zero ! 
      continue 
      if started and line.rstrip()=="End": 
      print "end at line", i 
      break 
      # process line 
      collected_lines.append(line.rstrip()) 

Il generatore enumerate prende un generatore ed enumera le iterazioni. Es.

print list(enumerate("a b c".split())) 

stampe

[ (0, "a"), (1,"b"), (2, "c") ] 

UPDATE:

il manifesto ha chiesto per l'utilizzo di una regex per abbinare linee come "===" e "======":

import re 
print re.match("^=+$", "===")  is not None 
print re.match("^=+$", "======") is not None 
print re.match("^=+$", "=")  is not None 
print re.match("^=+$", "=abc") is not None 
print re.match("^=+$", "abc=") is not None 
3

Ecco qualcosa che funziona:

data_file = open("test.txt") 
block = "" 
found = False 

for line in data_file: 
    if found: 
     block += line 
     if line.strip() == "End": break 
    else: 
     if line.strip() == "Start": 
      found = True 
      block = "Start" 

data_file.close() 
+0

questo è un trucco elegante – BPm

+3

@BPm: questo è un esempio di "macchina a stati finiti" (http://en.wikipedia.org/wiki/Finite_state_machine): la macchina si avvia in uno stato "Blocco non ancora trovato" (trovato == False), continua a funzionare in uno stato "All'interno del blocco" (trovato == True) e in questo caso si ferma quando viene trovato "Fine". Possono essere un po 'inefficienti (qui, 'found' deve essere controllato per ogni riga nel blocco), ma le macchine a stati spesso consentono di esprimere in modo pulito la logica di algoritmi più complessi. – EOL

+0

+1, perché questo è un buon esempio dell'approccio della macchina a stati completamente valido. – EOL

2

È possibile utilizzare una regex abbastanza facilmente. Puoi renderlo più robusto se necessario, di seguito è riportato un semplice esempio.

>>> import re 
>>> START = "some" 
>>> END = "Hello" 
>>> test = "this is some\nsample text\nthat has the\nwords Hello World\n" 
>>> m = re.compile(r'%s.*?%s' % (START,END),re.S) 
>>> m.search(test).group(0) 
'some\nsample text\nthat has the\nwords Hello' 
+0

+1: Ottima idea: questa è compatta e potrebbe essere molto efficiente, dal momento che il modulo 're' è veloce. I tag START e END dovrebbero essere forzati ad essere su una linea * da soli *, anche se nella tua espressione regolare ('^ ... $'). – EOL

+0

Grazie:) .. Non penso che tu possa usare^|| $ quando usi il re.S spec poiché include i caratteri di nuova riga, pensate che avreste bisogno di dire esplicitamente '% s \ n. *?% S \ n' – pyInTheSky

+1

Potete certamente usare^... $ in questo caso, semplicemente aggiungendo il flag re.MULTILINE (http://docs.python.org/dev/library/re.html#module-contents). – EOL

Problemi correlati