2011-11-16 19 views
17

Sembra che l'interfaccia mmap supporti solo readline(). Se provo ad eseguire iterazioni sull'oggetto, ottengo carattere anziché righe complete.Come leggere le righe dal file mmap in python?

Quale sarebbe il metodo "pythonic" per leggere un file mmap'line per riga?

import sys 
import mmap 
import os 


if (len(sys.argv) > 1): 
    STAT_FILE=sys.argv[1] 
    print STAT_FILE 
else: 
    print "Need to know <statistics file name path>" 
    sys.exit(1) 


with open(STAT_FILE, "r") as f: 
    map = mmap.mmap(f.fileno(), 0, prot=mmap.PROT_READ) 
    for line in map: 
    print line # RETURNS single characters instead of whole line 
+1

Fuori di interesse, qual è la motivazione per l'utilizzo di un file memory-mapped per questo, al contrario di un normale file? – NPE

+1

@aix: potrei avere GB di dati grezzi e mi piacerebbe accedervi nel modo più efficiente possibile. Ma la vera ragione è: è più figo :) –

+0

Non so se è più bello, ma non dovresti semplicemente pensare che sia più veloce (se ti interessa davvero, dovresti profilare). – NPE

risposta

22

Il modo più conciso per iterare le linee di un mmap è

with open(STAT_FILE, "r+b") as f: 
    map = mmap.mmap(f.fileno(), 0, prot=mmap.PROT_READ) 
    for line in iter(map.readline, ""): 
     # whatever 
+2

Non sapevo che 'iter' prendesse questa coppia di argomenti' callable'/'sentinel'. +1 e ho rimosso la mia risposta in favore di questo. –

+0

E per favore cambiate la modalità di apertura in 'r + b' invece di' r' (come menzionato nel mio post qui sotto). – hochl

+0

@hochl: Grazie, fatto. –

12

ho modificato il vostro esempio come questo:

with open(STAT_FILE, "r+b") as f: 
     m=mmap.mmap(f.fileno(), 0, prot=mmap.PROT_READ) 
     while True: 
       line=m.readline() 
       if line == '': break 
       print line.rstrip() 

suggerimenti:

  • non chiamare una variabile map, si tratta di una funzione built-in.
  • apre il file in r+b modo, come nell'esempio Python nella pagina di guida mmap. Si afferma: In entrambi i casi è necessario fornire un descrittore di file per un file aperto per l'aggiornamento. Vedi http://docs.python.org/library/mmap.html#mmap.mmap.
  • è meglio non utilizzare i nomi delle variabili globali UPPER_CASE_WITH_UNDERSCORES, come indicato in Nomi variabili globali allo http://www.python.org/dev/peps/pep-0008. In altri linguaggi di programmazione (come C), le costanti sono spesso scritte tutte maiuscole.

Spero che questo aiuti.

EDIT: Ho eseguito alcuni test di temporizzazione su Linux perché il commento mi ha incuriosito. Ecco un confronto tra i tempi eseguiti su 5 esecuzioni sequenziali su un file di testo di 137 MB.

# normal file access. 
real 2.410 2.414 2.428 2.478 2.490 
sys  0.052 0.052 0.064 0.080 0.152 
user 2.232 2.276 2.292 2.304 2.320 

# mmap file access. 
real 1.885 1.899 1.925 1.940 1.954 
sys  0.088 0.108 0.108 0.116 0.120 
user 1.696 1.732 1.736 1.744 1.752 

Quei tempi non comprendono il print dichiarazione (esclusi esso). Seguendo questi numeri, direi che l'accesso ai file mappati in memoria è un po 'più veloce.

EDIT 2: Utilizzando python -m cProfile test.py ho ottenuto i seguenti risultati:

5432833 2.273 0.000 2.273 0.000 {method 'readline' of 'file' objects} 
5432833 1.451 0.000 1.451 0.000 {method 'readline' of 'mmap.mmap' objects} 

Se non mi sbaglio, allora mmap è un po 'più veloce.

Inoltre, sembra che not len(line) funzioni peggio di line == '', almeno è così che interpreto l'output del profiler.

+0

'AttributeError: l'oggetto 'mmap.mmap' non ha attributo 'readlines'' –

+1

hochl: Grazie. I parametri di riferimento sono fantastici. Potresti allegare uno script per riprodurre il test e confermare l'analisi? –

+1

Ho semplicemente commentato la stampa nel tuo programma e poi ho eseguito 'time test.py' come 10 volte, poi ho preso i 5 valori medi. Sarebbe interessante controllare i risultati di 'python -m cProfile test.py'. – hochl

1

Quello che segue è abbastanza conciso:

with open(STAT_FILE, "r") as f: 
    m = mmap.mmap(f.fileno(), 0, prot=mmap.PROT_READ) 
    while True: 
     line = m.readline() 
     if line == "": break 
     print line 
    m.close() 

Nota che line conserva il ritorno a capo, così come si potrebbe rimuoverlo. È anche il motivo per cui if line == "" fa la cosa giusta (una riga vuota viene restituita come "\n").

Il motivo per cui l'iterazione originale funziona così è che mmap cerca di apparire come un file e una stringa. Sembra una stringa ai fini dell'iterazione.

Non ho idea del motivo per cui non può (o sceglie di non) fornire readlines()/xreadlines().

+0

Il metodo 'readlines()' degli oggetti file restituisce un elenco di tutte le linee del file. fare questo su un file con mmapping avrebbe completamente annullato lo scopo di mmap. –

+0

@SvenMarnach: Potrebbe essere un generatore. In ogni caso, per essere completamente onesto, non riesco a vedere la necessità di file mappati in memoria in questa intera domanda. – NPE

+0

Hai perfettamente ragione. Quindi forse la ragione della non esistenza di un tale generatore è che sarebbe inutile. :) –

Problemi correlati