Python中高效地分割大型文本文件的方法

7

这是一个之前的问题,在Python中提高函数的时间性能需要找到一种有效的方法来分割文本文件。

我有以下文本文件(超过32 GB),没有排序

....................
0 274 593869.99 6734999.96 121.83 1,
0 273 593869.51 6734999.92 121.57 1,
0 273 593869.15 6734999.89 121.57 1,
0 273 593868.79 6734999.86 121.65 1,
0 272 593868.44 6734999.84 121.65 1,
0 273 593869.00 6734999.94 124.21 1,
0 273 593868.68 6734999.92 124.32 1,
0 274 593868.39 6734999.90 124.44 1,
0 275 593866.94 6734999.71 121.37 1,
0 273 593868.73 6734999.99 127.28 1,
.............................

第一列和第二列是网格中x、y、z点的位置ID(例如:0-273)。
def point_grid_id(x,y,minx,maxy,distx,disty):
    """give id (row,col)"""
    col = int((x - minx)/distx)
    row = int((maxy - y)/disty)
    return (row, col)

(minx, maxx) 是我的网格的起点,大小为distx,disty。ID瓦片的数量是

tiles_id = [j for j in np.ndindex(ny, nx)] #ny = number of row, nx= number of columns 
from [(0,0),(0,1),(0,2),...,(ny-1,nx-1)]
n = len(tiles_id)

我需要将大小约为32GB的文件切片成n(=len(tiles_id))个文件。

虽然可以不用排序就读取文件n次,但出于这个原因,我希望找到一种从(0,0) (= tiles_id[0])开始的高效分割方法。之后,我只需要读取一次已经切割好的文件。


4
不用Python怎么样? - Bartek Banachewicz
3
希望你能找到读物,以便在那时它运行时可以阅读。 - Bartek Banachewicz
1
@Gianni:我喜欢Python!!,是啊,我能看出来,爱情是如此盲目,以至于你看不到它的弱点。 - Abhijit
@Abhijit和Bartek Banachewicz:“人生苦短,何必用C++编程” :) - Gianni Spear
@Abhijit,就我个人而言,我在这里没有看到Python的任何弱点展示,尽管我承认它们确实存在。问题是一个很好的例子,其中性能应该通过优化算法而不是代码来进行优化。只有在算法优化后仍然不能满意结果时,才有必要优化代码,甚至可能需要用C++重写它。 - Ellioh
显示剩余4条评论
2个回答

5

无论是使用 Python 还是命令行工具(sort),对于 32GB 的文件来说,排序都几乎不可能。数据库似乎太强大了,但也可用。然而,如果您不想使用数据库,我建议只需使用 tile id 将源文件拆分成文件。

您可以读取一行文本,以 tile id 为文件名,并将该行追加到文件中。继续执行此操作,直到源文件结束。虽然速度不会太快,但至少复杂度为 O(N),与排序不同。

当然,也可以单独对文件进行排序并将它们连接起来。在对 32GB 文件进行排序时,主要的瓶颈应该是内存而非 CPU。

就是这样,我认为:

def temp_file_name(l):
    id0, id1 = l.split()[:2]
    return "tile_%s_%s.tmp" % (id0, id1)

def split_file(name):
    ofiles = {}
    try:
        with open(name) as f:
            for l in f:
                if l:
                    fn = temp_file_name(l)
                    if fn not in ofiles:
                        ofiles[fn] = open(fn, 'w')
                    ofiles[fn].write(l)
    finally:
        for of in ofiles.itervalues():
            of.close()

split_file('srcdata1.txt')

但是如果有很多瓷砖,超过了您可以打开的文件数,您可以这样做:

def split_file(name):
    with open(name) as f:
        for l in f:
            if l:
                fn = temp_file_name(l)
                with open(fn, 'a') as of:
                    of.write(l)

最完美的方式是在打开文件数量达到限制后关闭一些文件并将它们从字典中删除。


3
可以的。您需要使用多文件排序和合并,但这是可能的。 - Martijn Pieters
@Ellioh,能请你举个代码示例吗?我不想浪费你的时间。(这有助于我学习其他人如何编码)。谢谢。 - Gianni Spear
@Ellioh 我认为有一个需要关闭的开放连接。 - Gianni Spear
@Ellioh。不,这只是我的错误。请问为什么要写“if l:”?我们可以跳过这一行吗?谢谢! - Gianni Spear
1
我的示例文件包含了一个空行。:-) 如果你的没有的话,你可以安全地省略 "if l:"。 - Ellioh
显示剩余8条评论

1
一次快速的谷歌搜索引导我找到了ActiveState code中这个食谱,它没有给出任何性能比较,但似乎可以胜任工作。
简而言之,它似乎做了@Ellioh建议的事情,并且你有一个现成的食谱,可能不需要重新发明轮子。

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