2

我有一个日志文件列表,其中每个文件中的每一行都有一个时间戳,并且这些行在每个文件中按升序排序。不同的文件可以有重叠的时间范围,我的目标是将它们混合到一个大文件中,按时间戳排序。排序中可能存在联系,在这种情况下,我希望下一行来自输入列表中首先列出的任何文件。

我已经看到了如何使用fileinput(请参阅此处)执行此操作的示例,但这似乎将所有文件读入内存。由于我的文件很大,这将是一个问题。因为我的文件是预先排序的,所以似乎应该有一种方法可以使用一种方法来合并它们,该方法只需要考虑每个文件中最近未探索的行。

4

2 回答 2

15

heapq.merge()如果标准库中有,为什么要自己滚动?不幸的是,它没有提供一个关键的论点——你必须自己做装饰 - 合并 - 不装饰的舞蹈:

from itertools import imap
from operator import itemgetter
import heapq

def extract_timestamp(line):
    """Extract timestamp and convert to a form that gives the
    expected result in a comparison
    """
    return line.split()[1] # for example

with open("log1.txt") as f1, open("log2.txt") as f2:
    sources = [f1, f2]
    with open("merged.txt", "w") as dest:
        decorated = [
            ((extract_timestamp(line), line) for line in f)
            for f in sources]
        merged = heapq.merge(*decorated)
        undecorated = imap(itemgetter(-1), merged)
        dest.writelines(undecorated)

上面的每一步都是“偷懒”的。因为我避免file.readlines()根据需要读取文件中的行。同样,使用生成器表达式而不是列表组合的装饰过程。heapq.merge()也是懒惰的——每个输入迭代器需要一个项目同时进行必要的比较。最后,我使用itertools.imap()了内置的 map() 的惰性变体来取消装饰。

(在 Python 3 中 map() 变得懒惰了,所以你可以使用它)

于 2012-09-17T15:01:08.983 回答
1

您想实现基于文件的合并排序。从两个文件中读取一行,输出旧行,然后从该文件中读取另一行。一旦其中一个文件用完,从另一个文件中输出所有剩余的行。

于 2012-09-17T14:04:45.410 回答