2010-02-24 10 views
11

Ich habe zwei große (~ 100 GB) Textdateien, die gleichzeitig durchlaufen werden müssen.zip() Alternative zum Iterieren durch zwei Iterables

Zip funktioniert gut für kleinere Dateien, aber ich fand heraus, dass es tatsächlich eine Liste von Zeilen aus meinen beiden Dateien erstellt. Dies bedeutet, dass jede Zeile im Speicher gespeichert wird. Ich brauche nichts mehr mit den Zeilen zu tun.

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. 

Gibt es eine Alternative zu zip(), der als Generator wirkt, die mich ohne> 200 GB RAM durch diese beiden Dateien zu durchlaufen können?

+0

... tatsächlich, ich weiß von einer Möglichkeit, aber es scheint nicht sehr Pythonic - während line1: line1 = handle1.readline(); line2 = handle2.readline(); mach etwas mit line1 und line2 ... –

+0

Apropos Umgebungen mit eingeschränktem Speicher, die du vielleicht interessant findest http://neopythonic.blogspot.com/2008/10/sorting-million-32-bit-integers-in-2mb.html –

Antwort

20

itertools hat eine Funktion izip so macht, dass

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

Wenn die Dateien unterschiedlicher Größe sind, können Sie izip_longest, wie izip verwenden wird in der kleineren Datei zu stoppen.

-1

So ähnlich? Wordy, aber es scheint das zu sein, wonach du verlangst.

Es kann angepasst werden, um Dinge wie eine richtige Zusammenführung zu tun, um die Schlüssel zwischen den beiden Dateien zu vergleichen, was oft mehr ist als die simple Zip-Funktion. Außerdem wird das nicht abgeschnitten, was wiederum der SQL OUTER JOIN-Algorithmus ist, der sich von dem unterscheidet, was zip-typisch ist und der für Dateien typisch ist.

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

Meinst du nicht 'während if1_more ODER if2_more:'? Und warum sollten Datei1 und Datei2 in Dateien eingebunden werden, wenn Dateien bereits vorhanden sind? Und schließlich, war das nur eine akademische "Wie würde ich das für mich tun, wenn ich musste?" Übung? Sicher würde man lieber izip oder izip_longest aus dem itertools-Modul in der std-lib verwenden, anstatt 20 Zeilen homebrewed-Code zu schreiben, der dasselbe tut, aber beibehalten und unterstützt (und debuggt werden müssen!). – PaulMcG

+0

@Paul McGuire: Ja, ODER ist korrekt.Der explizite Iter muss next verwenden und eine korrekte StopIteraction-Ausnahme bei EOF erhalten. Nein, das war nicht "akademisch". Dies ist eine Antwort auf die Frage. Die Frage ist vage und itertools bietet möglicherweise nicht die erforderlichen Funktionen. Dies kann auch nicht, aber dies kann zugeschnitten werden. –

+0

Ich führe Py2.5.4, und das Aufrufen von 'next()' auf einem Dateiobjekt am Ende der Datei löst StopIteration für mich. – PaulMcG

0

Wenn Sie auf die kürzeste Datei kürzen wollen:

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

Oder

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

Und wenn die beiden Dateien unterschiedliche Längen haben? Dies wird bei dem kürzeren abgeschnitten. Hoffentlich ist das das gewünschte Verhalten. –

+0

@ S.Lott: ist das nicht was 'zip' tut? – voyager

+0

@ S.Lott - das bricht nur aus der while-forever-Schleife heraus, wenn sowohl i_ended als auch j_ended, also bis zum Ende der längeren Datei gelesen wird. Aber es gibt definitiv Raum für Verbesserungen. Wenn eine Datei viel kürzer ist als die andere, ruft der aktuelle Code .next() auf und fängt StopIteration * viele Male, wenn wir bereits erfahren haben, dass die Datei beendet ist. Einfach genug zu tun: 'wenn nicht i_ended: versuchen Sie: i = handel1.next() ...' (wie Sie es in Ihrem 'if if_more:' Code tun). (Ah! Ich sehe, dass Ihr Kommentar auf den ursprünglichen Code reagierte, nicht auf die bearbeitete Version - Entschuldigung für das Einwerfen!) – PaulMcG

14

können Sie verwenden izip_longest wie dieses Pad die kürzere Datei mit leeren Zeilen

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

oder in 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 für 'mit' - Ich mag die Py3.1-Syntax, um die Einzugsebenen niedrig zu halten. – PaulMcG

0

Für python3, izip_longest ist eigentlich zip_longest.

from itertools import zip_longest 

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