[Python-es] Buscar, leer y escribir archivos grandes con Python

lasizoillo lasizoillo en gmail.com
Vie Jun 3 05:07:15 CEST 2011


El día 2 de junio de 2011 23:32, Manuel Enrique González Ramírez
<maengora en gmail.com> escribió:
> Hola a tod en s,
> Tengo la siguiente situación:
> Un archivo de 29Mb que contiene 281.351 lineas.  El archivo tiene la
> siguiente estructura:
> 1|2011-05-20 23:08:56|122711527|OPERADOR1|HOST TO HOST|212454|10000|HOST
> CLIENTE|192630167
> 2|2011-05-20 23:09:08|122711530|OPERADOR12|HOST TO
> HOST|57212454|1000|HOST CLIENTE|192630168
> 3|2011-05-20 23:09:42|122711538|OPERADOR1|HOST TO
> HOST|454545454545|2000|HOST CLIENTE|192630169
> 4|2011-05-20 23:10:03|122711544|OPERADOR1|HOST TO
> HOST|121221211|20000|HOST CLIENTE|192630170
> 5|2011-05-20 23:10:09|122711547|OPERADOR1|HOST TO HOST
> ESPECIAL|4545|5000|HOST CLIENTE|192630171
> 6|2011-05-20 23:10:46|122711554|OPERADOR1|HOST TO HOST
> ESPECIAL|545454445|5000|HOST CLIENTE|192630172
> .
> .
> Del cual debo capturar la última sección, es decir, el número que me
> representa una secuencia (192630167, 192630168, etc).
> Lo hago así:
> fc = open('archivo.txt','r')
> for linea in fc:
>     b = linea.rfind('|')
>     posi = b + 1
>     posf = posi + 9
>     secuenciac = linea[posi:posf]
>     print secuenciac

Supongo que esto es muy lento y que con el split habrás notado una
buena mejoría. Pero todavía se puede hacer mejor, rsplit es lo suyo:

In [9]: linea = "1|2011-05-20 23:08:56|122711527|OPERADOR1|HOST TO
HOST|212454|10000|HOST CLIENTE|192630167"

In [10]: timeit linea.split('|')[-1]
1000000 loops, best of 3: 1.2 us per loop

In [11]: timeit linea.rsplit('|',1)[-1]
1000000 loops, best of 3: 695 ns per loop

In [12]: linea.rsplit('|',1)[-1]
Out[12]: '192630167'

Para hacer esos mini-benchmarks tan chulos, se puede hacer facilmente
con ipython. Para un profiling mejor busca por san google temas de
python profiling e incluso python profiling kcachegrind. Lo suyo para
optimizar es poder medir en que estas gastando el tiempo ;-)

> Esto tarda en promedio 3 minutos (que es demasiado comparado con el tiempo
> que tarda [1min en promedio] con el script que me pasaron en Perl).
> Ahora, para sumarle al problema del tiempo que tarda dicha proceso, el
> resultado del ciclo anterior debe escribirse en otro archivo (datos.csv);
> eso lo hago así:
> # Abro un archivo para edición
>     fdif = open('datos.csv','a')
>     fdif.writelines('"'+secuenciac+'"; \n')
>     fdif.close()
> Y esto eleva exponencialmente el tiempo.  :(

No alcanzo a entender el uso de writelines:
http://docs.python.org/library/stdtypes.html#file.writelines

Supongo que no abres y cierras el fichero cada vez, pero tengo que
preguntarlo, ¿lo haces? Si lo estás haciendo, un profiler gritaría que
ese es el primer punto a optimizar.

> Estuve buscando en San Google y en un hilo anterior (así como en e foro de
> majibu) obtuve documentación donde se habla de lectura de archivos enormes
> con python usando un mínimo de tiempo:
> http://effbot.org/zone/wide-finder.htm
>
> def process(file, chunk):
>     f = open(file)
>     f.seek(chunk[0])
>     d = defaultdict(int)
>     for page in pat.findall(f.read(chunk[1])):
>         d[page] += 1
>     return
>
> Perdón pero no acabo de entender cómo implementar algo que me funcione
> utilizando el mínimo de tiempo como dicen que sucede por ejemplo con el
> process anterior.
> Se que se puede mejorar dichas situaciones pero lo poco que aún se de Python
> no me deja ser más eficiente (.. y es que mi ignorancia es infinita)
> Alguien me puede colaborar???
>

Creo que lo mejor es olvidarse un poco de conseguir la solución más
eficiente por ahora. Primero juega con python y cuando tengas soltura
irás haciendo el código más óptimo de forma fácil. Por ejemplo, la
optimización del rsplit no se me hubiera podido ocurrir sin conocer la
existencia del rsplit. Para saber por qué va más rápido hay que pensar
en la cantidad de objetos a crear/destruir en una y otra solución,
aparte de que una requiere un recorrido mayor de la cadena que la
otra.

Espero que el correo te haya resultado útil.

Un saludo:

Javi


Más información sobre la lista de distribución Python-es