Domanda

Esistono alternative al codice seguente:

startFromLine = 141978 # or whatever line I need to jump to

urlsfile = open(filename, "rb", 0)

linesCounter = 1

for line in urlsfile:
    if linesCounter > startFromLine:
        DoSomethingWithThisLine(line)

    linesCounter += 1

Se sto elaborando un enorme file di testo (~15MB) con righe di lunghezza sconosciuta ma diversa e devo passare a una riga particolare quale numero conosco in anticipo? Mi sento male elaborandoli uno per uno quando so che potrei ignorare almeno la prima metà del file. Alla ricerca di una soluzione più elegante se ce n'è.

È stato utile?

Soluzione

linecache :

  

Il linecache consente di ottenere qualsiasi linea da un file sorgente Python, mentre tenta di ottimizzare internamente, usando una cache, il caso comune in cui molte righe vengono lette da un singolo file. Viene utilizzato dal traceback per recuperare le linee di origine per l'inclusione nel traceback formattato ...

Altri suggerimenti

Non puoi saltare avanti senza leggere il file almeno una volta, poiché non sai dove si trovano le interruzioni di riga. Potresti fare qualcosa del tipo:

# Read in the file once and build a list of line offsets
line_offset = []
offset = 0
for line in file:
    line_offset.append(offset)
    offset += len(line)
file.seek(0)

# Now, to skip to line n (with the first line being line 0), just do
file.seek(line_offset[n])

Non hai davvero tante opzioni se le linee sono di diversa lunghezza ... purtroppo devi elaborare i caratteri di fine riga per sapere quando sei passato alla riga successiva.

È possibile, tuttavia, velocizzare notevolmente E ridurre l'utilizzo della memoria modificando l'ultimo parametro in " apri " a qualcosa che non è 0.

0 indica che l'operazione di lettura dei file è senza buffer, che è molto lenta e richiede molto tempo sul disco. 1 significa che il file è bufferizzato in linea, il che sarebbe un miglioramento. Qualsiasi cosa sopra 1 (diciamo 8k .. cioè: 8096 o superiore) legge blocchi di file in memoria. Puoi ancora accedervi tramite for line in open(etc):, ma Python va un po 'alla volta, scartando ogni blocco bufferizzato dopo che è stato elaborato.

Probabilmente sono viziato da ram abbondanti, ma 15 M non sono enormi. Leggere in memoria con readlines() è quello che faccio di solito con file di queste dimensioni. Accedere a una riga dopo è banale.

Dato che non c'è modo di determinare la lunghezza di tutte le righe senza leggerle, non hai altra scelta che iterare su tutte le righe prima della tua linea di partenza. Tutto quello che puoi fare è farlo sembrare bello. Se il file è davvero enorme, potresti voler utilizzare un approccio basato sul generatore:

from itertools import dropwhile

def iterate_from_line(f, start_from_line):
    return (l for i, l in dropwhile(lambda x: x[0] < start_from_line, enumerate(f)))

for line in iterate_from_line(open(filename, "r", 0), 141978):
    DoSomethingWithThisLine(line)

Nota: l'indice è zero in base a questo approccio.

Sono sorpreso che nessuno abbia menzionato Islice

line = next(itertools.islice(Fhandle,index_of_interest,index_of_interest+1),None) # just the one line

o se vuoi tutto il resto del file

rest_of_file = itertools.islice(Fhandle,index_of_interest)
for line in rest_of_file:
    print line

o se vuoi ogni altra riga dal file

rest_of_file = itertools.islice(Fhandle,index_of_interest,None,2)
for odd_line in rest_of_file:
    print odd_line

Se si conosce in anticipo la posizione nel file (piuttosto il numero di riga), è possibile utilizzare file.seek () per andare in quella posizione.

Modifica : puoi utilizzare la linecache. getline (nomefile, lineno) , che restituirà il contenuto della linea lineno, ma solo dopo aver letto l'intero file in memoria. Buono se stai accedendo in modo casuale alle linee dall'interno del file (come lo stesso python potrebbe voler fare per stampare un traceback) ma non va bene per un file da 15 MB.

Se non vuoi leggere l'intero file in memoria .. potrebbe essere necessario trovare un formato diverso dal testo normale.

ovviamente tutto dipende da cosa stai cercando di fare e dalla frequenza con cui passerai attraverso il file.

Ad esempio, se salterai alle linee molte volte nello stesso file e sai che il file non cambia mentre lavori con esso, puoi farlo:
Innanzitutto, passa attraverso l'intero file e registra & Quot; seek-location & Quot; di alcuni numeri di riga-chiave (come ad esempio, sempre 1000 righe),
Quindi, se vuoi la linea 12005, passa alla posizione 12000 (che hai registrato), quindi leggi 5 righe e saprai di essere nella riga 12005 e così via

Cosa genera il file che si desidera elaborare? Se è qualcosa sotto il tuo controllo, potresti generare un indice (quale linea è in quale posizione.) Nel momento in cui il file viene aggiunto. Il file indice può avere una dimensione di linea fissa (spazio riempito o 0 numeri imbottiti) e sarà sicuramente più piccolo. E così può essere letto ed elaborato in modo rapido.

  • Quale linea vuoi ?.
  • Calcola l'offset di byte del numero di riga corrispondente nel file indice (possibile perché la dimensione della riga del file indice è costante).
  • Usa cerca o altro per saltare direttamente per ottenere la linea dal file indice.
  • Analizza per ottenere l'offset dei byte per la riga corrispondente del file effettivo.

Ho avuto lo stesso problema (ho bisogno di recuperare dalla riga specifica del file enorme).

Sicuramente, posso sempre scorrere tutti i record nel file e fermarlo quando il contatore sarà uguale alla linea di destinazione, ma non funziona in modo efficace nel caso in cui desideri ottenere un numero plurale di righe specifiche. Ciò ha causato la risoluzione del problema principale: come gestire direttamente nel luogo necessario del file.

Ho scoperto la prossima decisione: Innanzitutto ho completato il dizionario con la posizione iniziale di ogni riga (la chiave è il numero di riga e il valore & # 8211; lunghezza cumulata delle righe precedenti).

t = open(file,’r’)
dict_pos = {}

kolvo = 0
length = 0
for each in t:
    dict_pos[kolvo] = length
    length = length+len(each)
    kolvo = kolvo+1

in definitiva, funzione obiettivo:

def give_line(line_number):
    t.seek(dict_pos.get(line_number))
    line = t.readline()
    return line

t.seek (line_number) & # 8211; comando che esegue la potatura del file fino all'inizio della riga. Quindi, se commetti successivamente readline & # 8211; ottieni la tua linea di destinazione.

Usando tale approccio ho risparmiato una parte significativa del tempo.

Le righe stesse contengono informazioni sull'indice? Se il contenuto di ciascuna riga era simile a & Quot; <line index>:Data & Quot ;, quindi l'approccio seek() potrebbe essere utilizzato per effettuare una ricerca binaria attraverso il file, anche se la quantità di Data è variabile. Dovresti cercare il punto medio del file, leggere una riga, verificare se il suo indice è superiore o inferiore a quello desiderato, ecc.

Altrimenti, il meglio che puoi fare è solo readlines(). Se non vuoi leggere tutti i 15 MB, puoi utilizzare l'argomento sizehint per sostituire almeno molti readline() s con un numero inferiore di chiamate a <=>.

Ecco un esempio usando 'readlines (sizehint)' per leggere un pezzo di linee alla volta. DNS ha sottolineato quella soluzione. Ho scritto questo esempio perché gli altri esempi qui sono orientati a linea singola.

def getlineno(filename, lineno):
    if lineno < 1:
        raise TypeError("First line is line 1")
    f = open(filename)
    lines_read = 0
    while 1:
        lines = f.readlines(100000)
        if not lines:
            return None
        if lines_read + len(lines) >= lineno:
            return lines[lineno-lines_read-1]
        lines_read += len(lines)

print getlineno("nci_09425001_09450000.smi", 12000)

È possibile utilizzare mmap per trovare l'offset delle linee. MMap sembra essere il modo più veloce per elaborare un file

Esempio:

with open('input_file', "r+b") as f:
    mapped = mmap.mmap(f.fileno(), 0, prot=mmap.PROT_READ)
    i = 1
    for line in iter(mapped.readline, ""):
        if i == Line_I_want_to_jump:
            offsets = mapped.tell()
        i+=1

quindi usa f.seek (offset) per spostarti sulla linea che ti serve

Se hai a che fare con un file di testo & amp; basato su sistema linux , potresti usare i comandi linux.
Per me, ha funzionato bene!

import commands

def read_line(path, line=1):
    return commands.getoutput('head -%s %s | tail -1' % (line, path))

line_to_jump = 141978
read_line("path_to_large_text_file", line_to_jump)

Può usare questa funzione per restituire la riga n:

def skipton(infile, n):
    with open(infile,'r') as fi:
        for i in range(n-1):
            fi.next()
        return fi.next()
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top