2011-11-16 22 views
17

Parece que la interfaz mmap solo es compatible con readline(). Si trato de iterar sobre el objeto obtengo carácter en lugar de líneas completas.¿Cómo leer líneas del archivo mmap en python?

¿Cuál sería el método "pythonic" de leer un archivo mmap'ed línea por línea?

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

Como curiosidad, ¿cuál es la motivación para el uso de un archivo asignado en memoria para esto, en lugar de un archivo normal? – NPE

+1

@aix: posiblemente podría tener GB de datos brutos, y me gustaría acceder a ellos en el método más eficiente posible. Pero la verdadera razón es: es más genial :) –

+0

No sé si es más fresco, pero no debes simplemente asumir que es más rápido (si realmente te importa, deberías hacer un perfil). – NPE

Respuesta

22

La forma más concisa para repetir las líneas de un mmap es

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

No sabía que 'iter' tomó este par de argumentos' invocable'/'sentinel'. +1 y eliminó mi respuesta a favor de este. –

+0

Y cambie el modo abierto a 'r + b' en lugar de' r' (como se menciona en mi publicación a continuación). – hochl

+0

@hochl: Gracias, hecho. –

12

he modificado su ejemplo como este:

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() 

Sugerencias:

  • no llaman una variable map, esta es una función incorporada.
  • abra el archivo en r+b modo, como en el ejemplo de Python en la página de ayuda mmap. Establece: En cualquier caso, debe proporcionar un descriptor de archivo para un archivo abierto para la actualización. Ver http://docs.python.org/library/mmap.html#mmap.mmap.
  • es mejor no utilizar nombres de variables globales UPPER_CASE_WITH_UNDERSCORES, como se menciona en Nombres de variable globales en http://www.python.org/dev/peps/pep-0008. En otros lenguajes de programación (como C), las constantes a menudo se escriben en mayúsculas.

Espero que esto ayude.

EDIT: Hice algunas pruebas de tiempo en Linux porque el comentario me dio curiosidad. Aquí hay una comparación de los tiempos realizados en 5 ejecuciones secuenciales en un archivo de texto de 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 

Esos tiempos no incluyen el print declaración (I excluido). Siguiendo estos números, diría que el acceso al archivo mapeado en memoria es bastante más rápido.

EDIT 2: Usandopython -m cProfile test.py me dio los siguientes resultados:

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} 

Si no me equivoco entonces mmap es un poco más rápido.

Además, parece que not len(line) tiene un rendimiento inferior a line == '', al menos así es como interpreto la salida del generador de perfiles.

+0

'AttributeError: 'mmap.mmap' objeto no tiene atributo 'readlines'' –

+1

hochl: Gracias. Los puntos de referencia son geniales. ¿Podría adjuntar una secuencia de comandos para reproducir la prueba y confirmar el análisis? –

+1

Simplemente comenté la impresión en su programa y luego hice 'time test.py' como 10 veces, luego tomé los 5 valores medios. Sería interesante comprobar los resultados de 'python -m cProfile test.py'. – hochl

1

El siguiente es razonablemente concisa:

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() 

Tenga en cuenta que line conserva el carácter de nueva línea, por lo que le gustaría eliminarlo. También es la razón por la cual if line == "" hace lo correcto (se devuelve una línea vacía como "\n").

La razón por la que la iteración original funciona de la manera en que lo hace es porque mmap intenta verse como un archivo y una cadena. Parece una cadena a los efectos de la iteración.

No tengo idea de por qué no puede (o elige no) proporcionar readlines()/xreadlines().

+0

El método 'readlines()' de objetos de archivos devuelve una lista de todas las líneas del archivo. hacer esto en un archivo mmapped sería completamente contrario al propósito de mmap. –

+0

@SvenMarnach: podría ser un generador. En cualquier caso, para ser totalmente honesto, no veo la necesidad de archivos mapeados en memoria en toda esta pregunta. – NPE

+0

Tiene toda la razón. Entonces, tal vez la razón de la inexistencia de un generador de este tipo es que no tendría sentido. :) –