2010-02-24 5 views
11

J'ai deux grands fichiers texte (~ 100 Go) qui doivent être itérés simultanément. Zip fonctionne bien pour les fichiers plus petits, mais j'ai découvert qu'il fait en fait une liste de lignes à partir de mes deux fichiers. Cela signifie que chaque ligne est stockée en mémoire. Je n'ai pas besoin de faire quoi que ce soit avec les lignes plus d'une fois.zip() alternative pour itération à travers deux iterables

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. 

est-il une alternative à zip() qui agit comme un générateur qui me permettra de itérer ces deux fichiers sans utiliser> 200 Go de RAM?

+0

... en fait, je connais d'une certaine façon mais cela ne semble pas très pythonique - while line1: line1 = handle1.readline(); line2 = handle2.readline(); faire quelque chose avec line1 et line2 ... –

+0

En parlant d'environnements contraints en mémoire, vous pourriez trouver cela intéressant http://neopythonic.blogspot.com/2008/10/sorting-million-32-bit-integers-in-2mb.html –

Répondre

20

itertools a une fonction izip qui fait que

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

Si les fichiers sont de tailles différentes, vous pouvez utiliser izip_longest, comme izip arrêtera le fichier plus petit.

-1

Quelque chose comme ça? Wordy, mais il semble que ce soit ce que vous demandez.

Il peut être ajusté pour faire des choses comme une fusion appropriée pour faire correspondre les clés entre les deux fichiers, ce qui est souvent plus nécessaire que la fonction zip simpliste. En outre, cela ne tronque pas, ce qui est ce que l'algorithme SQL OUTER JOIN, encore une fois, différent de ce que fait zip et plus typique des fichiers.

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

Vous ne vouliez pas dire 'alors que if1_more OU if2_more:'? Et pourquoi encapsuler file1 et file2 dans les itéraux, quand les fichiers sont déjà des itérations? Et enfin, était-ce juste un universitaire "comment ferais-je cela pour moi si je devais le faire?" exercice? On préfèrerait sûrement utiliser izip ou izip_longest du module itertools dans std lib, au lieu d'écrire 20 lignes de code homebrewed qui font la même chose, mais qui devraient être maintenues et supportées (et déboguées!). – PaulMcG

+0

@ Paul McGuire: Oui, OU est correct.L'iter explicite est nécessaire pour utiliser next et obtenir une exception StopIteraction appropriée à EOF. Non ce n'était pas "académique". Ceci est une réponse à la question. La question est vague et les outils peuvent ne pas fournir les fonctionnalités requises. Cela ne peut pas non plus, mais cela peut être adapté. –

+0

Je cours Py2.5.4, et appeler 'next()' sur un objet de fichier à la fin du fichier soulève StopIteration pour moi. – PaulMcG

0

Si vous voulez tronquer au plus court fichier:

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

Ou

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

Et si les deux fichiers sont de longueurs différentes? Cela tronquera à la plus courte. Espérons que c'est le comportement souhaité. –

+0

@ S.Lott: n'est-ce pas ce que «zip» fait? – voyager

+0

@ S.Lott - cela ne fait que sortir de la boucle while-forever lorsque les deux sont i_ended et j_ended, donc il sera lu jusqu'à la fin du fichier plus long. Mais il y a certainement place à amélioration. Si un fichier est beaucoup plus court que l'autre, le code actuel appelle .next() et arrête StopIteration * plusieurs * fois, quand nous avons déjà appris que le fichier est terminé. Assez simple à faire: 'if if i_ended: try: i = handel1.next() ...' (comme vous le faites dans votre code if if1_more: '). (Ah, je vois que votre commentaire répondait au code original, pas à la version éditée - désolé de m'avancer!) – PaulMcG

14

Vous pouvez utiliser izip_longest comme celui-ci au pad le fichier plus court avec des lignes vides

en python 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=""): 
      ... 

ou 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 pour 'with' - J'aime la syntaxe Py3.1 pour maintenir les niveaux de retrait. – PaulMcG

0

Pour python3, izip_longest est en fait zip_longest.

from itertools import zip_longest 

for i, j in izip(handle1, handle2): 
    ... 
Questions connexes