2010-02-24 4 views
11

У меня есть два больших (~ 100 ГБ) текстовых файлов, которые необходимо повторить одновременно.zip() альтернатива для итерации через два итератора

Zip хорошо работает для небольших файлов, но я узнал, что на самом деле он делает список строк из моих двух файлов. Это означает, что каждая строка сохраняется в памяти. Мне больше не нужно ничего делать с линиями.

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. 

Есть ли альтернатива молнии(), который выступает в качестве генератора, который позволит мне перебирать этих двух файлов без использования> 200GB оперативной памяти?

+0

... на самом деле, я знаю один способ, но он не кажется очень pythonic - while line1: line1 = handle1.readline(); line2 = handle2.readline(); сделать что-то с line1 и line2 ... –

+0

Говоря о средах с ограниченной памятью, вы можете найти это интересное http://neopythonic.blogspot.com/2008/10/sorting-million-32-bit-integers-in-2mb.html –

ответ

20

itertools имеет функцию izip, что делает это

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

Если файлы различных размеров, вы можете использовать izip_longest как izip остановится на меньший размер файла.

-1

Что-то вроде этого? Wordy, но, похоже, это то, о чем вы просите.

Он может быть настроен так, чтобы делать что-то вроде правильного слияния, чтобы сопоставлять ключи между двумя файлами, что зачастую является более необходимым, чем упрощенная функция zip. Кроме того, это не усекает, что и алгоритм SQL OUTER JOIN, опять же отличается от того, что делает zip и более типичным для файлов.

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

Разве вы не имели в виду «while if1_more OR if2_more:»? И зачем обертывать файлы file1 и file2 в iters, когда файлы уже есть? И, наконец, это было просто академическое «как я сделаю это для себя, если бы мне пришлось?» упражнение? Конечно, вы предпочли бы использовать izip или izip_longest из модуля itertools в std lib, вместо того, чтобы писать 20 строк кода на дому, который делает то же самое, но его нужно поддерживать и поддерживать (и отлаживать!). – PaulMcG

+0

@Paul McGuire: Да, ИЛИ правильно.Явный iter должен использовать следующий и получить правильное исключение StopIteraction в EOF. Нет, это не было «академическим». Это ответ на вопрос. Вопрос нечеткий, и itertools могут не предоставлять требуемые функции. Это может и не быть, но это может быть адаптировано. –

+0

Я запускаю Py2.5.4, а вызов 'next()' в файловом объекте в конце файла вызывает для меня StopIteration. – PaulMcG

0

Если вы хотите укоротить кратчайшему файла:

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

Или

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

И если два файла различной длины? Это сократится на более коротком. Надеюсь, это желаемое поведение. –

+0

@ S.Lott: это не то, что делает 'zip'? – voyager

+0

@ S.Lott - это только выходит из цикла while-forever, когда оба i_ended AND j_ended, поэтому он будет читать до конца более длинного файла. Но есть определенная возможность для улучшения. Если один файл намного короче другого, текущий код вызовет .next() и поймает StopIteration * много * раз, когда мы уже узнали, что файл закончился. Достаточно просто сделать: 'if not i_ended: try: i = handel1.next() ...' (как вы это делаете в коде 'if if1_more:'). (Ах! Я вижу, что ваш комментарий отвечал на исходный код, а не на отредактированную версию - извините за включение!) – PaulMcG

14

Вы можете использовать izip_longest как это раздутькороче файл с пустыми строками

в питона 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=""): 
      ... 

или 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 для 'with' - Мне нравится синтаксис Py3.1, чтобы удерживать уровни отступа. – PaulMcG

0

Для Python3, izip_longest на самом деле zip_longest.

from itertools import zip_longest 

for i, j in izip(handle1, handle2): 
    ... 
Смежные вопросы