2010-02-24 40 views
11

Tôi có hai tệp văn bản lớn (~ 100 GB) phải được lặp lại thông qua cùng một lúc.zip() thay thế cho lặp qua hai lần lặp

Zip hoạt động tốt cho các tệp nhỏ hơn nhưng tôi phát hiện ra rằng nó thực sự tạo danh sách các dòng từ hai tệp của tôi. Điều này có nghĩa là mọi dòng được lưu trữ trong bộ nhớ. Tôi không cần phải làm bất cứ điều gì với các dòng hơn một lần.

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. 

Có cách nào khác để zip() hoạt động như một trình tạo cho phép tôi lặp qua hai tệp này mà không sử dụng> 200 GB RAM không?

+0

... thực sự, tôi biết một cách nhưng có vẻ như không phải là pythonic - trong khi line1: line1 = handle1.readline(); line2 = handle2.readline(); làm điều gì đó với line1 và line2 ... –

+0

Phát biểu về môi trường bị hạn chế về bộ nhớ, bạn có thể tìm thấy http://neopythonic.blogspot.com/2008/10/sorting-million-32-bit-integers-in-2mb.html –

Trả lời

20

itertools có chức năng izip nào đó

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

Nếu các tập tin có kích thước khác nhau mà bạn có thể sử dụng izip_longest, như izip sẽ dừng lại ở tập tin nhỏ hơn.

-1

Một cái gì đó như thế này? Wordy, nhưng nó có vẻ là những gì bạn đang yêu cầu.

Nó có thể được điều chỉnh để thực hiện những việc như hợp nhất phù hợp để khớp các phím giữa hai tệp, thường là những gì cần thiết hơn chức năng zip đơn giản. Ngoài ra, điều này không cắt ngắn, đó là những gì các thuật toán SQL OUTER JOIN, một lần nữa, khác với những gì zip hiện và điển hình hơn của các tập tin.

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

thú vị này Bạn không có ý 'trong khi if1_more OR if2_more:'? Và tại sao lại bao bọc file1 và file2 trong iters, khi các tập tin đã được iters? Và cuối cùng, đây chỉ là một học thuật "làm thế nào tôi sẽ làm điều này cho bản thân mình nếu tôi đã phải?" tập thể dục? Chắc chắn một người thích sử dụng izip hoặc izip_longest từ mô-đun itertools trong thư viện std, thay vì viết 20 dòng mã homebrewed làm điều tương tự, nhưng sẽ phải được duy trì và hỗ trợ (và gỡ lỗi!). – PaulMcG

+0

@Paul McGuire: Có, HOẶC là chính xác.Lặp lại rõ ràng là cần thiết để sử dụng tiếp theo và nhận được một ngoại lệ StopIteraction thích hợp tại EOF. Không có điều này không phải là "học tập". Đây là câu trả lời cho câu hỏi. Câu hỏi là mơ hồ và itertools có thể không cung cấp các tính năng cần thiết. Điều này có thể không, nhưng điều này có thể được điều chỉnh. –

+0

Tôi đang chạy Py2.5.4 và gọi 'next()' trên một đối tượng tệp ở cuối tệp sẽ làm tăng StopIteration cho tôi. – PaulMcG

0

Nếu bạn muốn cắt ngắn đến tập tin ngắn:

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

khác

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

Hoặc

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

Và nếu hai tệp có độ dài khác nhau thì sao? Điều này sẽ cắt ngắn ở cái ngắn hơn. Hy vọng rằng, đó là hành vi mong muốn. –

+0

@ S.Lott: không phải là những gì 'zip' làm gì? – voyager

+0

@ S.Lott - điều này chỉ xảy ra trong vòng lặp while-forever khi cả i_ended AND j_ended, vì vậy nó sẽ đọc cho đến khi kết thúc tệp dài hơn. Nhưng chắc chắn có chỗ để cải thiện. Nếu một tệp ngắn hơn nhiều so với tệp kia, mã hiện tại sẽ gọi .next() và bắt StopIteration * nhiều lần *, khi chúng tôi đã biết rằng tệp đã kết thúc. Đơn giản, đủ để làm: 'if not i_ended: try: i = handel1.next() ...' (như bạn làm trong 'if if1_more:' code). (Ah! Tôi thấy rằng nhận xét của bạn đã phản hồi mã gốc, chứ không phải phiên bản đã chỉnh sửa - xin lỗi vì đã can thiệp!) – PaulMcG

14

Bạn có thể sử dụng izip_longest như thế này để pad file ngắn hơn với các dòng trống

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

hoặc trong 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 cho 'with' - Tôi thích cú pháp Py3.1 để giảm mức thụt lề. – PaulMcG

0

Đối python3, izip_longest thực sự là zip_longest.

from itertools import zip_longest 

for i, j in izip(handle1, handle2): 
    ... 
Các vấn đề liên quan