create - gzip python example
Una forma más limpia de leer/gunzip un archivo enorme en python (2)
Así que tengo algunos archivos .gz bastante gigantescos: estamos hablando de 10 a 20 gb cada uno cuando se descomprimen.
Necesito recorrer cada línea de ellos, así que estoy usando el estándar:
import gzip
f = gzip.open(path+myFile, ''r'')
for line in f.readlines():
#(yadda yadda)
f.close()
Sin embargo, los comandos open() y close() toman AGES, usando hasta el 98% de la memoria + CPU. Tanto es así que el programa sale e imprime Killed to the terminal. ¿Tal vez está cargando todo el archivo extraído en la memoria?
Ahora estoy usando algo como:
from subprocess import call
f = open(path+''myfile.txt'', ''w'')
call([''gunzip'', ''-c'', path+myfile], stdout=f)
#do some looping through the file
f.close()
#then delete extracted file
Esto funciona. ¿Pero hay una forma más limpia?
Echa un vistazo a los pandas, en particular las herramientas IO . Son compatibles con la compresión gzip al leer archivos y puede leer archivos en trozos. Además, los pandas son muy rápidos y eficientes en la memoria.
Como nunca lo intenté, no sé qué tan bien viven juntos la compresión y la lectura en fragmentos, pero podría valer la pena intentarlo.
Estoy 99% seguro de que su problema no está en gzip.open() , sino en readlines() .
Como explica la documentación :
f.readlines () devuelve una lista que contiene todas las líneas de datos en el archivo.
Obviamente, eso requiere leer leer y descomprimir todo el archivo, y construir una lista absolutamente gigantesca.
Lo más probable es que, en realidad, las llamadas a malloc para asignar toda esa memoria se lleven para siempre. Y luego, al final de este alcance (asumiendo que estás usando CPython), tiene que GC toda esa lista gigantesca, que también durará para siempre.
Casi nunca quieres usar readlines . A menos que estés usando un Python muy antiguo, haz esto:
for line in f:
Un file es un iterable lleno de líneas, al igual que la list devuelta por las líneas de lectura. Excepto que en realidad no es una list , genera más líneas sobre la marcha mediante la lectura de un búfer. Por lo tanto, en un momento dado, solo tendrá una línea y un par de búferes del orden de 10 MB cada uno, en lugar de una list 25 GB. Y la lectura y la descompresión se extenderán a lo largo de la vida útil del bucle, en lugar de hacerlo de una vez.
Desde una prueba rápida, con un archivo gzip de gzip.open() , gzip.open() es efectivamente instantáneo, for line in f: pass toma unos segundos, gzip.close() es efectivamente instantánea. Pero si hago for line in f.readlines(): pass , me toma ... bueno, no estoy seguro de cuánto tiempo, porque después de un minuto mi sistema entró en el infierno y tuve que matar al intérprete haz que responda a cualquier cosa ...
Dado que esto ha surgido una docena de veces más desde esta respuesta, escribí esta publicación del blog que explica un poco más.