我有一个巨大的文本文件(超过100演出),有6列数据(制表符作为分隔符)。在第一列中,我有整数值(设置中有2500个不同的值)。我需要根据第一列中的值将此文件拆分为多个较小的文件(请注意,行未进行排序)。这些较小的文件中的每一个都将用于在matlab中准备绘图。
我只有8 GB的ram。
问题是如何有效地做到这一点?有什么想法吗?
答案 0 :(得分:5)
使用bash:
cat 100gigfile | while read line; do
intval="$( echo "$line" | cut -f 1)"
chunkfile="$( printf '%010u.txt' "$intval" )"
echo "$line" >> "$chunkfile"
done
这会将您的100 gig文件拆分为(如您所说)2500个根据第一个字段的值命名的单个文件。您可能需要根据自己的喜好调整printf格式参数。
答案 1 :(得分:2)
单行与bash + awk:
awk '{print $0 >> $1".dat" }' 100gigfile
这会将大文件的每一行附加到名为第一列的值+“。dat”扩展名的文件中,例如第12 aa bb cc dd ee ff
行将转到12.dat
文件。
答案 2 :(得分:1)
对于linux 64位(我不确定它是否适用于Windows),您可以mmap该文件,并将块复制到新文件。我认为这是最有效的方式。
答案 3 :(得分:0)
在你的shell中......
$ split -d -l <some number of lines> Foo Foo
这会将大文件Foo
拆分为Foo1
到FooN
,其中n由原始行数除以您提供给-l的值确定。在循环中迭代碎片......
编辑 ...评论中的好点...此脚本(下方)将逐行读取,分类并根据第一个字段分配给文件...
#!/usr/bin/env python
import csv
prefix = 'filename'
reader = csv.reader(open('%s.csv' % prefix, 'r'))
suffix = 0
files = {}
# read one row at a time, classify on first field, and send to a file
# row[0] assumes csv reader does *not* split the line... if you make it do so,
# remove the [0] indexing (and strip()s) below
for row in reader:
tmp = row[0].split('\t')
fh = files.get(tmp[0].strip(), False)
if not fh:
fh = open('%s%05i.csv' % (prefix, suffix), 'a')
files[tmp[0].strip()] = fh
suffix += 1
fh.write(row[0])
for key in files.keys():
files[key].close()
答案 4 :(得分:0)
最有效的方法是逐块打开,一次打开所有文件,并重新使用读取缓冲区进行写入。在提供信息时,数据中没有其他模式可用于加速它。
您将在不同的文件描述符中打开每个文件,以避免每行打开和关闭。一开始就打开它们,或者在你走的时候懒洋洋地打开它们。完成之前关闭它们。默认情况下,大多数Linux发行版只允许1024个打开的文件,所以你必须使用ulimit -n 2600
,因为你有权这样做(参见/etc/security/limits.conf
)。
将一个缓冲区(比如几个kb)和源文件中的原始读取分配到其中。迭代但保留控制变量。无论何时到达终点或缓冲区的末尾,都要从缓冲区写入正确的文件描述符。您需要考虑几个边缘情况,例如当读取获取换行但不足以确定应该进入哪个文件时。
如果计算出最小行大小,可以反向迭代以避免处理缓冲区的前几个字节。尽管如此,这将证明有点棘手但速度加快。
我想知道非阻塞I / O是否能解决这个问题。
答案 5 :(得分:0)
显而易见的解决方案是每次遇到新值时打开一个新文件,并保持打开直到结束。但是您的操作系统可能不允许您一次打开2500个文件。所以如果你只需要这样做一次,你就可以这样做:
list[StartIndex]
到list[StartIndex+99]
。list[StartIndex] <= value <= list[StartIndex+99]
输入这些记录。StartIndex
添加100,如果尚未完成,请转到第3步。所以你需要26次传递文件。