如何使用Python对大型文件进行排序?

4
我在activestate.com上找到了一些有前途的用于排序大文件的代码。我正在尝试在Ubuntu 10.04上默认安装的Python 2.6.5解释器上运行它。当我尝试在一个小测试文件上运行它时,我收到了下面的错误跟踪信息。我在activestate.com上寻求了帮助,但这个帖子已经静默了超过18个月。这里是否有任何人看到了一个明显的解决方案?
谢谢。
## {{{ http://code.activestate.com/recipes/576755/ (r3)
# based on Recipe 466302: Sorting big files the Python 2.4 way
# by Nicolas Lehuen

import os
from tempfile import gettempdir
from itertools import islice, cycle
from collections import namedtuple
import heapq

Keyed = namedtuple("Keyed", ["key", "obj"])

def merge(key=None, *iterables):
    # based on code posted by Scott David Daniels in c.l.p.
    # http://groups.google.com/group/comp.lang.python/msg/484f01f1ea3c832d

    if key is None:
        keyed_iterables = iterables
    else:
        keyed_iterables = [(Keyed(key(obj), obj) for obj in iterable)
                            for iterable in iterables]

    for element in heapq.merge(*keyed_iterables):
        yield element.obj


def batch_sort(input, output, key=None, buffer_size=32000, tempdirs=None):
    if tempdirs is None:
        tempdirs = []
    if not tempdirs:
        tempdirs.append(gettempdir())

    chunks = []
    try:
        with open(input,'rb',64*1024) as input_file:
            input_iterator = iter(input_file)
            for tempdir in cycle(tempdirs):
                current_chunk = list(islice(input_iterator,buffer_size))
                if not current_chunk:
                    break
                current_chunk.sort(key=key)
                output_chunk = open(os.path.join(tempdir,'%06i'%len(chunks)),'w+b',64*1024)
                chunks.append(output_chunk)
                output_chunk.writelines(current_chunk)
                output_chunk.flush()
                output_chunk.seek(0)
        with open(output,'wb',64*1024) as output_file:
            output_file.writelines(merge(key, *chunks))
    finally:
        for chunk in chunks:
            try:
                chunk.close()
                os.remove(chunk.name)
            except Exception:
                pass

错误跟踪:

Traceback (most recent call last):
  File "./batch_sort.py", line 108, in <module>
    batch_sort(args[0],args[1],options.key,options.buffer_size,options.tempdirs)
  File "./batch_sort.py", line 54, in batch_sort
    output_file.writelines(merge(key, *chunks))
  File "./batch_sort.py", line 30, in merge
    yield element.obj
AttributeError: 'str' object has no attribute 'obj'

你不确定“huge”是什么意思,所以我会把它理解为“巨大的”。如果你真的要对大文件进行排序,最好不要使用Python。由于其解释性质和动态存储分配,这可能会使其在执行此操作时变得缓慢。去找一个独立的排序工具;这些工具旨在尽可能快地对大量数据进行排序。 - Ira Baxter
好问题。我将“巨大”定义为一个包含1400万行或更多行的UTF-8文件,每行平均175个字符,总计2.5到7.5 GB(许多文件都有3字节的UTF-8字符)。另一种选择是使用Linux sort从bash脚本/终端。旧版本代码的性能还可以,但这个版本应该更快。 - tahoar
有人可能想要检查这个库,它在纯Python中实现了外部排序。 - RomainL.
1个回答

2
合并的代码是不正确的。 如果您不提供一个键,每个元素将是一个字符串而不是一个带键的元组。
尝试使用以下代码替代:
def merge(key=None, *iterables):
    # based on code posted by Scott David Daniels in c.l.p.
    # http://groups.google.com/group/comp.lang.python/msg/484f01f1ea3c832d

    if key is None:
        for element in heapq.merge(*iterables):
            yield element
    else:
        keyed_iterables = [(Keyed(key(obj), obj) for obj in iterable)
                        for iterable in iterables]
        for element in heapq.merge(*keyed_iterables):
            yield element.obj

@tahoar 我正在使用相同的脚本来对大文件进行排序。运行时,我在第51行遇到了错误: with open(output,'wb',64*1024) as output_file: output_file.writelines(merge(key, *chunks)) valueerror: I/O操作关闭了文件。你见过这个错误吗?不过,对于小文件,排序工作得很好! - Think
@Think,事实上,我放弃了这个努力。每当我达到更大的文件大小时,就会遇到新的问题。由于我只需要在Linux上使用此功能,我的最终解决方案使用Python的subprocess.Popen()调用Linux的“sort”应用程序,所有问题都消失了。很抱歉我无法提供更多帮助。 - tahoar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接