2010-02-24 12 views
11

Tengo dos archivos de texto grandes (~ 100 GB) que deben repetirse simultáneamente.zip() alternativa para iterar a través de dos iterables

Zip funciona bien para archivos más pequeños, pero descubrí que en realidad está haciendo una lista de líneas de mis dos archivos. Esto significa que cada línea se almacena en la memoria. No necesito hacer nada con las líneas más de una vez.

handle1 = open('filea', 'r'); handle2 = open('fileb', 'r') 

for i, j in zip(handle1, handle2): 
    do something with i and j. 
    write to an output file. 
    no need to do anything with i and j after this. 

¿Existe una alternativa a zip() que actúa como un generador que me permitirá iterar a través de estos dos archivos sin utilizar> 200 GB de RAM?

+0

... en realidad, sé de una sola manera, pero no parece muy pitónico - mientras que la línea 1: línea 1 = handle1.readline(); line2 = handle2.readline(); hacer algo con line1 y line2 ... –

+0

Hablando de entornos con limitaciones de memoria, puede encontrar este interesante http://neopythonic.blogspot.com/2008/10/sorting-million-32-bit-integers-in-2mb.html –

Respuesta

20

itertools tiene una función que hace que izip

from itertools import izip 
for i, j in izip(handle1, handle2): 
    ... 

Si los archivos son de diferentes tamaños puede utilizar izip_longest, como izip se detendrá en el archivo más pequeño.

-1

¿Algo como esto? Wordy, pero parece ser lo que estás pidiendo.

Se puede ajustar para hacer cosas como una combinación adecuada para unir las teclas entre los dos archivos, que a menudo es más lo que se necesita que la función zip simplista. Además, esto no se trunca, que es lo que hace el algoritmo SQL OUTER JOIN, una vez más, diferente de lo que hace zip y más típico de los archivos.

with open("file1","r") as file1: 
    with open("file2", "r" as file2: 
     for line1, line2 in parallel(file1, file2): 
      process lines 

def parallel(file1, file2): 
    if1_more, if2_more = True, True 
    while if1_more or if2_more: 
     line1, line2 = None, None # Assume simplistic zip-style matching 
     # If you're going to compare keys, then you'd do that before 
     # deciding what to read. 
     if if1_more: 
      try: 
       line1= file1.next() 
      except StopIteration: 
       if1_more= False 
     if if2_more: 
      try: 
       line2= file2.next() 
      except StopIteration: 
       if2_more= False 
     yield line1, line2 
+3

¿No quisiste decir "mientras if1_more OR if2_more:"? ¿Y por qué envolver file1 y file2 en iters, cuando los archivos ya son iters? Y, por último, ¿era solo un académico "cómo podría hacer esto por mí mismo si tuviera que hacerlo?" ¿ejercicio? Seguramente uno preferiría usar izip o izip_longest del módulo itertools en std lib, en lugar de escribir 20 líneas de código casero que haga lo mismo, pero debería mantenerse y soportarse (¡y depurarse!). – PaulMcG

+0

@Paul McGuire: Sí, O es correcto.Se requiere el iter explícito para usar next y obtener una excepción adecuada StopIteraction en EOF. No, esto no fue "académico". Esta es una respuesta a la pregunta. La pregunta es vaga e itertools puede no proporcionar las características requeridas. Esto tampoco puede, pero esto se puede adaptar. –

+0

Estoy ejecutando Py2.5.4, y al llamar a 'next()' en un objeto de archivo al final del archivo, aparece StopIteration para mí. – PaulMcG

0

Si desea truncar en el archivo más corto:

handle1 = open('filea', 'r') 
handle2 = open('fileb', 'r') 

try: 
    while 1: 
     i = handle1.next() 
     j = handle2.next() 

     do something with i and j. 
     write to an output file. 

except StopIteration: 
    pass 

finally: 
    handle1.close() 
    handle2.close() 

Else

handle1 = open('filea', 'r') 
handle2 = open('fileb', 'r') 

i_ended = False 
j_ended = False 
while 1: 
    try: 
     i = handle1.next() 
    except StopIteration: 
     i_ended = True 
    try: 
     j = handle2.next() 
    except StopIteration: 
     j_ended = True 

     do something with i and j. 
     write to an output file. 
    if i_ended and j_ended: 
     break 

handle1.close() 
handle2.close() 

O

handle1 = open('filea', 'r') 
handle2 = open('fileb', 'r') 

while 1: 
    i = handle1.readline() 
    j = handle2.readline() 

    do something with i and j. 
    write to an output file. 

    if not i and not j: 
     break 
handle1.close() 
handle2.close() 
+0

¿Y si los dos archivos tienen longitudes diferentes? Esto se truncará en el más corto. Con suerte, ese es el comportamiento deseado. –

+0

@ S.Lott: ¿no es eso lo que 'zip' hace? – voyager

+0

@ S.Lott: esto solo se interrumpe en el ciclo while cuando i_ended AND j_ended, por lo que se leerá hasta el final del archivo más largo. Pero definitivamente hay margen de mejora. Si un archivo es mucho más corto que el otro, el código actual llamará a .next() y atrapará StopIteration * muchas * veces, cuando ya hayamos aprendido que el archivo ha finalizado. Lo suficientemente simple como para hacer: 'if not i_ended: try: i = handel1.next() ...' (como lo hace en su código 'if if1_more:'). (¡Ah! Veo que su comentario respondía al código original, no a la versión editada, ¡lo siento por meterme!) – PaulMcG

14

Usted puede utilizar izip_longest como esto para rellenar el archivo más corto con líneas vacías

en pitón 2,6

from itertools import izip_longest 
with handle1 as open('filea', 'r'): 
    with handle2 as open('fileb', 'r'): 
     for i, j in izip_longest(handle1, handle2, fillvalue=""): 
      ... 

o en python3.1

from itertools import izip_longest 
with handle1 as open('filea', 'r'), handle2 as open('fileb', 'r'): 
    for i, j in izip_longest(handle1, handle2, fillvalue=""): 
     ... 
+0

+1 for 'with' - Me gusta la sintaxis de Py3.1 para mantener los niveles de indentación. – PaulMcG

0

Para python3, izip_longest es en realidad zip_longest.

from itertools import zip_longest 

for i, j in izip(handle1, handle2): 
    ... 
Cuestiones relacionadas