Leggere solo righe specifiche


Risposte:


253

Se il file da leggere è grande e non si desidera leggere l'intero file in memoria contemporaneamente:

fp = open("file")
for i, line in enumerate(fp):
    if i == 25:
        # 26th line
    elif i == 29:
        # 30th line
    elif i > 29:
        break
fp.close()

Si noti che i == n-1per la nlinea th.


In Python 2.6 o successivo:

with open("file") as fp:
    for i, line in enumerate(fp):
        if i == 25:
            # 26th line
        elif i == 29:
            # 30th line
        elif i > 29:
            break

8
enumerate(x)utilizza x.next, quindi non necessita dell'intero file in memoria.
Alok Singhal,

3
La mia piccola carne di manzo è che A) vuoi usare con la coppia aperta / stretta e quindi mantenere il corpo corto, B) Ma il corpo non è così corto. Sembra un compromesso tra velocità / spazio ed essere Pythonic. Non sono sicuro di quale sarebbe la soluzione migliore.
Hamish Grubijan,

5
con è sopravvalutato, il pitone andava d'accordo per oltre 13 anni senza di essa
Dan D.

38
@Dan D. L'elettricità è sopravvalutata, l'umanità è andata d'accordo per oltre 200 mila anni senza di essa. ;-) 'with' lo sta rendendo più sicuro, più leggibile e una riga più breve.
Romain Vincent,

9
perché usare per loop, non penso che tu capisca il significato di big file. Il ciclo richiederà anni per raggiungere l'indice
devssh

159

La risposta rapida:

f=open('filename')
lines=f.readlines()
print lines[25]
print lines[29]

o:

lines=[25, 29]
i=0
f=open('filename')
for line in f:
    if i in lines:
        print i
    i+=1

Esiste una soluzione più elegante per l'estrazione di molte righe: linecache (per gentile concessione di "python: come passare a una riga particolare in un enorme file di testo?" , Una domanda precedente su stackoverflow.com).

Citando la documentazione di Python collegata sopra:

>>> import linecache
>>> linecache.getline('/etc/passwd', 4)
'sys:x:3:3:sys:/dev:/bin/sh\n'

Cambia il 4numero di riga desiderato e il gioco è fatto. Si noti che 4 porterebbe la quinta riga poiché il conteggio è a base zero.

Se il file potrebbe essere molto grande e causare problemi durante la lettura in memoria, potrebbe essere una buona idea seguire i consigli di @ Alok e utilizzare enumerate () .

Concludere:

  • Utilizzare fileobject.readlines()o for line in fileobjectcome soluzione rapida per file di piccole dimensioni.
  • Utilizzare linecacheper una soluzione più elegante, che sarà abbastanza veloce per la lettura di molti file, possibile più volte.
  • Prendere @ consiglio e l'uso di Alokenumerate() per i file che potrebbero essere molto grande, e non si adatta in memoria. Si noti che l'utilizzo di questo metodo potrebbe rallentare perché il file viene letto in sequenza.

7
Bello. Ho appena guardato l'origine del linecachemodulo e sembra che legga l'intero file in memoria. Quindi, se l'accesso casuale è più importante dell'ottimizzazione delle dimensioni, linecacheè il metodo migliore.
Alok Singhal,

7
con linecache.getlin ('some_file', 4) Ottengo la 4a riga, non la 5a.
Juan,

fatto curioso: se usi un set invece dell'elenco nel secondo esempio, otterrai O (1) tempo di esecuzione. Cerca in un elenco è O (n). I set interni sono rappresentati come hash, ecco perché ottieni il tempo di esecuzione O (1). non è un grosso problema in questo esempio, ma se si utilizza un ampio elenco di numeri e si cura dell'efficienza, i set sono la strada da percorrere.
Rady,

linecacheora sembra funzionare solo con i file sorgente di Python
Paul H,

Puoi anche usare linecache.getlines('/etc/passwd')[0:4]per leggere la prima, la seconda, la terza e la quarta riga.
zyy

30

Un approccio rapido e compatto potrebbe essere:

def picklines(thefile, whatlines):
  return [x for i, x in enumerate(thefile) if i in whatlines]

questo accetta qualsiasi oggetto simile a un file aperto thefile(lasciando al chiamante se deve essere aperto da un file su disco o tramite ad esempio un socket o un altro flusso simile a un file) e un insieme di indici di linea a base zero whatlinese restituisce un elenco, con ingombro di memoria ridotto e velocità ragionevole. Se il numero di righe da restituire è enorme, potresti preferire un generatore:

def yieldlines(thefile, whatlines):
  return (x for i, x in enumerate(thefile) if i in whatlines)

che è sostanzialmente buono solo per fare un ciclo su - nota che l'unica differenza deriva dall'uso di parentesi arrotondate anziché quadrate nell'istruzione return, facendo rispettivamente una comprensione della lista e un'espressione del generatore.

Inoltre, nonostante la menzione di "linee" e "file", queste funzioni sono molto, molto più generali: funzioneranno su qualsiasi iterabile, sia esso un file aperto o altro, restituendo un elenco (o generatore) di elementi in base al numero progressivo degli articoli. Quindi, suggerirei di usare nomi più appropriatamente generali ;-).


@ephemient, non sono d'accordo - il genexp legge senza problemi e perfettamente.
Alex Martelli,

Soluzione eccellente ed elegante, grazie! In effetti, anche i file di grandi dimensioni dovrebbero essere supportati, con l'espressione del generatore. Non puoi diventare più elegante di così, vero? :)
Samuel Lampa,

Bella soluzione, come si confronta con quella proposta da @AdamMatan? La soluzione Adam potrebbe essere più veloce in quanto sfrutta informazioni aggiuntive (numeri di riga che aumentano monotonicamente) che potrebbero portare a una fermata anticipata. Ho un file da 10 GB che non posso caricare in memoria.
Mannaggia,

2
@Mannaggia Non è abbastanza enfatizzato in questa risposta, ma whatlinesdovrebbe essere un set, perché if i in whatlinesverrà eseguito più velocemente con un set piuttosto che un elenco (ordinato). Non me ne sono accorto prima e invece ho ideato la mia brutta soluzione con un elenco ordinato (in cui non ho dovuto scansionare un elenco ogni volta, mentre lo if i in whatlinesfa), ma la differenza di prestazioni era trascurabile (con i miei dati) e questo la soluzione è molto più elegante.
Victor K,

28

Per offrire un'altra soluzione:

import linecache
linecache.getline('Sample.txt', Number_of_Line)

Spero che sia facile e veloce :)


1
Spero che questa sia la soluzione più ottimale.
maniac_user

2
Questo legge l'intero file in memoria. Puoi anche chiamare file.read (). Split ('\ n') quindi utilizzare le ricerche dell'indice dell'array per ottenere la linea di interesse ...
duhaime

Potresti fornire un esempio @duhaime
anon

14

se vuoi la linea 7

line = open ("file.txt", "r"). readlines () [7]

14
Neat. Ma come si fa ad close()aprire il file in questo modo?
Milo Wielondek,

1
@ 0sh dobbiamo chiudere?
Ooker

1
sì. dopo dobbiamo chiudere. Quando apriamo un file usando "con" ... si chiude da solo.
Reetesh11,

10

Per completezza, ecco un'altra opzione.

Cominciamo con una definizione dai documenti di Python :

slice Un oggetto che di solito contiene una porzione di una sequenza. Viene creata una sezione utilizzando la notazione del pedice, [] con due punti tra i numeri quando ne vengono indicati diversi, come in nome_varia [1: 3: 5]. La notazione parentesi (pedice) utilizza oggetti slice internamente (o nelle versioni precedenti, __getslice __ () e __setslice __ ()).

Sebbene la notazione delle sezioni non sia direttamente applicabile agli iteratori in generale, il itertoolspacchetto contiene una funzione di sostituzione:

from itertools import islice

# print the 100th line
with open('the_file') as lines:
    for line in islice(lines, 99, 100):
        print line

# print each third line until 100
with open('the_file') as lines:
    for line in islice(lines, 0, 100, 3):
        print line

Il vantaggio aggiuntivo della funzione è che non legge l'iteratore fino alla fine. Quindi puoi fare cose più complesse:

with open('the_file') as lines:
    # print the first 100 lines
    for line in islice(lines, 100):
        print line

    # then skip the next 5
    for line in islice(lines, 5):
        pass

    # print the rest
    for line in lines:
        print line

E per rispondere alla domanda originale:

# how to read lines #26 and #30
In [365]: list(islice(xrange(1,100), 25, 30, 4))
Out[365]: [26, 30]

1
Di gran lunga l'approccio migliore quando si lavora con file di grandi dimensioni. Il mio programma è passato dal consumo di 8 GB + a quasi nulla. Il compromesso è stato l'utilizzo della CPU che è passato dal ~ 15% al ​​~ 40% ma l'elaborazione effettiva del file è stata del 70% più veloce. Prenderò quel compromesso tutto il giorno. Grazie! 🎉🎉🎉
GollyJer,

1
Questo mi sembra il più pitonico. Grazie!
ipetrik,

10

La lettura dei file è incredibilmente veloce. La lettura di un file da 100 MB richiede meno di 0,1 secondi (vedere il mio articolo Lettura e scrittura di file con Python ). Quindi dovresti leggerlo completamente e quindi lavorare con le singole righe.

Ciò che la maggior parte delle risposte qui non è sbagliato, ma cattivo stile. L'apertura dei file dovrebbe sempre essere eseguita conwith poiché garantisce che il file venga nuovamente chiuso.

Quindi dovresti farlo in questo modo:

with open("path/to/file.txt") as f:
    lines = f.readlines()
print(lines[26])  # or whatever you want to do with this line
print(lines[30])  # or whatever you want to do with this line

File enormi

Se ti capita di avere un enorme file e il consumo di memoria è un problema, puoi elaborarlo riga per riga:

with open("path/to/file.txt") as f:
    for i, line in enumerate(f):
        pass  # process line i

IMO è uno stile davvero pessimo leggere un intero file di lunghezza sconosciuta, solo per ottenere le prime 30 righe .. che dire del consumo di memoria .. e che dire dei flussi infiniti?
ritorno42

@ return42 Dipende molto dall'applicazione. Per molti, è del tutto corretto supporre che un file di testo abbia dimensioni molto inferiori rispetto alla memoria disponibile. Se ti capita di avere file potenzialmente enormi, ho modificato la mia risposta.
Martin Thoma,

grazie per la tua aggiunta, che è la stessa di ogni risposta . E scusa no, non penso che questo dipenda dall'applicazione. IMO è sempre meglio non leggere più righe di quelle che ti servono.
ritorno42

7

Alcuni di questi sono adorabili, ma può essere fatto molto più semplicemente:

start = 0 # some starting index
end = 5000 # some ending index
filename = 'test.txt' # some file we want to use

with open(filename) as fh:
    data = fin.readlines()[start:end]

print(data)

Questo userà semplicemente il list slicing, caricherà l'intero file, ma la maggior parte dei sistemi minimizzerà in modo appropriato l'utilizzo della memoria, è più veloce della maggior parte dei metodi indicati sopra e funziona sui miei file di dati 10G +. In bocca al lupo!


4

È possibile effettuare una chiamata seek () che posiziona la testina di lettura su un byte specificato all'interno del file. Questo non ti aiuterà se non sai esattamente quanti byte (caratteri) sono scritti nel file prima della riga che vuoi leggere. Forse il tuo file è rigorosamente formattato (ogni riga è il numero X di byte?) Oppure potresti contare tu stesso il numero di caratteri (ricordati di includere caratteri invisibili come le interruzioni di riga) se vuoi davvero aumentare la velocità.

Altrimenti, devi leggere ogni riga prima della riga che desideri, secondo una delle tante soluzioni già proposte qui.


3

Se il tuo file di testo di grandi dimensioni fileè rigorosamente ben strutturato (il che significa che ogni riga ha la stessa lunghezza l), puoi utilizzare per n-th line

with open(file) as f:
    f.seek(n*l)
    line = f.readline() 
    last_pos = f.tell()

Dichiarazione di non responsabilità Funziona solo con file della stessa lunghezza!


2

Cosa ne pensi di questo:

>>> with open('a', 'r') as fin: lines = fin.readlines()
>>> for i, line in enumerate(lines):
      if i > 30: break
      if i == 26: dox()
      if i == 30: doy()

È vero, questo è meno efficiente di quello di Alok, ma il mio usa una dichiarazione with;)
Hamish Grubijan,

2

Se non ti dispiace importare, fileinput fa esattamente quello che ti serve (questo è possibile leggere il numero di riga della riga corrente)


2
def getitems(iterable, items):
  items = list(items) # get a list from any iterable and make our own copy
                      # since we modify it
  if items:
    items.sort()
    for n, v in enumerate(iterable):
      if n == items[0]:
        yield v
        items.pop(0)
        if not items:
          break

print list(getitems(open("/usr/share/dict/words"), [25, 29]))
# ['Abelson\n', 'Abernathy\n']
# note that index 25 is the 26th item

Roger, il mio ragazzo preferito! Ciò potrebbe beneficiare di una dichiarazione with.
Hamish Grubijan,

2

Preferisco questo approccio perché è più generico, cioè puoi usarlo su un file, sul risultato di f.readlines(), su un StringIOoggetto, qualunque cosa:

def read_specific_lines(file, lines_to_read):
   """file is any iterable; lines_to_read is an iterable containing int values"""
   lines = set(lines_to_read)
   last = max(lines)
   for n, line in enumerate(file):
      if n + 1 in lines:
          yield line
      if n + 1 > last:
          return

>>> with open(r'c:\temp\words.txt') as f:
        [s for s in read_specific_lines(f, [1, 2, 3, 1000])]
['A\n', 'a\n', 'aa\n', 'accordant\n']

2

Ecco i miei piccoli 2 centesimi, per quello che vale;)

def indexLines(filename, lines=[2,4,6,8,10,12,3,5,7,1]):
    fp   = open(filename, "r")
    src  = fp.readlines()
    data = [(index, line) for index, line in enumerate(src) if index in lines]
    fp.close()
    return data


# Usage below
filename = "C:\\Your\\Path\\And\\Filename.txt"
for line in indexLines(filename): # using default list, specify your own list of lines otherwise
    print "Line: %s\nData: %s\n" % (line[0], line[1])

2

Un cambiamento migliore e minore per la risposta di Alok Singhal

fp = open("file")
for i, line in enumerate(fp,1):
    if i == 26:
        # 26th line
    elif i == 30:
        # 30th line
    elif i > 30:
        break
fp.close()


1

@OP, puoi usare enumerate

for n,line in enumerate(open("file")):
    if n+1 in [26,30]: # or n in [25,29] 
       print line.rstrip()

1
file = '/path/to/file_to_be_read.txt'
with open(file) as f:
    print f.readlines()[26]
    print f.readlines()[30]

Usando l'istruzione with, questo apre il file, stampa le linee 26 e 30, quindi chiude il file. Semplice!


questa non è una risposta valida. dopo che la prima chiamata readlines()all'iteratore sarà esaurita e la seconda chiamata restituirà un elenco vuoto o genererà un errore (non ricordo quale)
Paolo H

1

Puoi farlo molto semplicemente con questa sintassi che qualcuno ha già menzionato, ma è di gran lunga il modo più semplice per farlo:

inputFile = open("lineNumbers.txt", "r")
lines = inputFile.readlines()
print (lines[0])
print (lines[2])

1

Per stampare la linea n. 3,

line_number = 3

with open(filename,"r") as file:
current_line = 1
for line in file:
    if current_line == line_number:
        print(file.readline())
        break
    current_line += 1

Autore originale: Frank Hofmann


1

Abbastanza veloce e al punto.

Per stampare determinate righe in un file di testo. Creare un elenco "lines2print" e quindi stampare quando l'enumerazione è "nell'elenco" di lines2print. Per sbarazzarti di '\ n' extra usa line.strip () o line.strip ('\ n'). Mi piace solo la "comprensione delle liste" e provo a usarla quando posso. Mi piace il metodo "with" per leggere i file di testo per evitare di lasciare un file aperto per qualsiasi motivo.

lines2print = [26,30] # can be a big list and order doesn't matter.

with open("filepath", 'r') as fp:
    [print(x.strip()) for ei,x in enumerate(fp) if ei in lines2print]

o se la lista è piccola basta digitare la lista come lista nella comprensione.

with open("filepath", 'r') as fp:
    [print(x.strip()) for ei,x in enumerate(fp) if ei in [26,30]]

0

Per stampare la linea desiderata. Per stampare la riga sopra / sotto la riga richiesta.

def dline(file,no,add_sub=0):
    tf=open(file)
    for sno,line in enumerate(tf):
        if sno==no-1+add_sub:
         print(line)
    tf.close()

esegui ----> dline ("D: \ dummy.txt", 6) cioè dline ("percorso file", numero_stampa, se vuoi la linea superiore della linea cercata, dai 1 per -1 inferiore, questo è il valore predefinito opzionale essere preso 0)


0

Se desideri leggere righe specifiche, ad esempio la riga che inizia dopo alcune righe di soglia, puoi utilizzare i seguenti codici, file = open("files.txt","r") lines = file.readlines() ## convert to list of lines datas = lines[11:] ## raed the specific lines


-1
f = open(filename, 'r')
totalLines = len(f.readlines())
f.close()
f = open(filename, 'r')

lineno = 1
while lineno < totalLines:
    line = f.readline()

    if lineno == 26:
        doLine26Commmand(line)

    elif lineno == 30:
        doLine30Commmand(line)

    lineno += 1
f.close()

7
questo non è così sintonico.
SilentGhost,

Fornisce un risultato errato, in quanto non è possibile utilizzare readline e readline in questo modo (ognuno cambia la posizione di lettura corrente).

Mi dispiace di aver trascurato un errore ENORME nel mio primo codice. L'errore è stato corretto e il codice corrente dovrebbe funzionare come previsto. Grazie per aver segnalato il mio errore, Roger Pate.
inspectorG4dget,

-1

Penso che funzionerebbe

 open_file1 = open("E:\\test.txt",'r')
 read_it1 = open_file1.read()
 myline1 = []
 for line1 in read_it1.splitlines():
 myline1.append(line1)
 print myline1[0]

C'erano già una dozzina di metodi readline quando l'hai postato - l'aggiunta di un altro aggiunge solo disordine
duhaime
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.