2012-03-09 23 views
14

Ho uno script molto semplice in questo momento che conta le righe di un file di testo utilizzando enumerate():(Python) Contare le linee in un enorme (10 GB>) file più velocemente possibile

i = 0 
f = open("C:/Users/guest/Desktop/file.log", "r") 
for i, line in enumerate(f): 
     pass 
print i + 1 
f.close() 

Questo richiede circa 3 e un mezzo minuto per passare attraverso un file di registro da 15 GB con ~ 30 milioni di righe. Sarebbe bello se potessi ottenere questo sotto due minuti o meno, perché questi sono registri giornalieri e vogliamo fare un'analisi mensile, quindi il codice dovrà elaborare 30 log di ~ 15 GB - più di un'ora e mezza forse e vorremmo ridurre al minimo il tempo di caricamento della memoria & sul server.

vorrei anche accontentarsi di un metodo di buona approssimazione/stima, ma ha bisogno di essere di circa 4 in ordine a fig accurata ...

Grazie!

+3

In generale, sarebbe probabilmente più veloce trattare il file come dati binari, leggerlo in blocchi di dimensioni ragionevoli (ad esempio 4KB alla volta) e contare i caratteri \ n in ogni blocco mentre si va. – aroth

+4

Questo non è un risultato migliore della tua soluzione ingenua, ma fyi il modo pietonico di scrivere quello che hai qui sarebbe semplicemente 'con open (fname) come f: print sum (1 per line in f)' – wim

+1

aroth: Grazie per il suggerimento, dovrei esaminarlo. wim: fantastico, grazie, è molto più breve ... – Adrienne

risposta

17

Ignacio's answer è corretto, ma potrebbe non riuscire se si dispone di un processo a 32 bit.

Ma forse potrebbe essere utile leggere il file in blocco e contare i caratteri \n in ciascun blocco.

def blocks(files, size=65536): 
    while True: 
     b = files.read(size) 
     if not b: break 
     yield b 

with open("file", "r") as f: 
    print sum(bl.count("\n") for bl in blocks(f)) 

farà il vostro lavoro.

Nota che non apro il file come file binario, pertanto \r\n verrà convertito in \n, rendendo il conteggio più affidabile.

+1

Proprio come un punto dati, una lettura di un grande file di circa 51 MB è passata da circa un minuto utilizzando l'approccio naive a meno di un secondo utilizzando questo approccio. –

+3

@MKatz Che ora, "un file di grandi dimensioni" o "un file di circa 51 MB"?;-) – glglgl

+0

questa soluzione potrebbe perdere l'ultima riga ma potrebbe non essere importante per un file enorme. –

5

mmap il file e contare le nuove righe.

6

So che è un po 'ingiusto, ma si potrebbe fare questo

int(subprocess.check_output("wc -l C:\\alarm.bat").split()[0]) 

Se la vostra sulle finestre Coreutils

+0

La mia soluzione richiede solo 1m37 in tempo reale. –

+1

questo è molto più veloce –

+0

Sembra che tu debba fare 'int (subprocess.check_output ("/usr/bin/wc -l cred ", shell = True) .split() [0])' per python3 – ZN13

0

Una soluzione veloce, 1-line è:

sum((1 for i in open(file_path, 'rb'))) 

Dovrebbe funzionare su file di dimensioni arbitrarie.

1

mi piacerebbe estendere la risposta di GL ed eseguire il suo/la sua codice utilizzando il modulo multiprocessing Python per conteggio veloce:

def blocks(f, cut, size=64*1024): # 65536 
    start, chunk =cut 
    iter=0 
    read_size=int(size) 
    _break =False 
    while not _break: 
     if _break: break 
     if f.tell()+size>start+chunk: 
      read_size=int(start+chunk- f.tell()) 
      _break=True 
     b = f.read(read_size) 
     iter +=1 
     if not b: break 
     yield b 


def get_chunk_line_count(data): 
    fn, chunk_id, cut = data 
    start, chunk =cut 
    cnt =0 
    last_bl=None 

    with open(fn, "r") as f: 
     if 0: 
      f.seek(start) 
      bl = f.read(chunk) 
      cnt= bl.count('\n') 
     else: 
      f.seek(start) 
      for i, bl in enumerate(blocks(f,cut)): 
       cnt += bl.count('\n') 
       last_bl=bl 

     if not last_bl.endswith('\n'): 
      cnt -=1 

     return cnt 
.... 
pool = multiprocessing.Pool(processes=pool_size, 
          initializer=start_process, 
          ) 
pool_outputs = pool.map(get_chunk_line_count, inputs) 
pool.close() # no more tasks 
pool.join() 

Ciò permetterà di migliorare le prestazioni a contare 20 pieghe. L'ho spostato su un script e lo metto su Github.

Problemi correlati