我有一个非常庞大的文件(> 5亿行),我希望根据其中一列的前3个字符拆分成几个较小的文件。
看起来像这样,第1列和第2列的每个元素都是唯一的:
A0A023GPI8 A0A023GPI8.1 232300 1027923628
A0A023GPJ0 A0A023GPJ0.2 716541 765680613
A0A023PXA5 A0A023PXA5.1 559292 728048729
A0A023PXB0 A0A023PXB0.1 559292 728048786
A0A023PXB5 A0A023PXB5.1 559292 728048524
A0A023PXB9 A0A023PXB9.1 559292 728048769
A0A023PXC2 A0A023PXC2.1 559292 728050382
我使用下面的脚本认为它会很快,因为在我看来它只涉及整个文件的单个读取。然而,它已经运行了几天而且还远未完成。有什么想法可以解释为什么,提出解决方案吗?
while read line
do
PREFIX=$(echo "$line" | cut -f2 | cut -c1-3)
echo -e "$line" >> ../split_DB/$PREFIX.part
done < $file
答案 0 :(得分:3)
read
效率不高;它必须一次读取一个字符以避免读取下一个换行符。但是,这里有很大的开销来源是每行调用cut
两次。我们可以通过再次使用read
进行拆分,并使用参数扩展来提取第二列的第一个字符来避免这种情况。
while read -r line; do
read -r _ col2 _ <<< "$line"
prefix=${col2:0:3}
# If the first column has a fixed width, you can forgo the
# previous two lines and use
# prefix=${line:12:3}
printf '%s\n' "$line" >> ../split_DB/$prefix.part
done < "$file"
但是,我不会花费太多时间在bash
中有效地执行此操作:这是一个快速而肮脏的Python脚本,它将执行相同的操作:
output_files = {}
with open(file) as fh:
for line in fh:
cols = line.strip().split()
prefix = cols[1][0:3]
# Cache the output file handles, so that each
# is opened only once.
outfh = output_files.setdefault(prefix, open("../split_DB/{}.part".format(prefix), "w"))
print(line, file=outfh)
# Close all the output files
for f in output_files.values():
f.close()
答案 1 :(得分:3)
这很容易:
$ awk '{s=substr($2,1,3); print >> s}' file
>>
重定向打印以按给定的名称附加文件。名称由第二列的前3个字母组成。
这将比Bash处理此文件更快。
通常,操作系统对打开的同时文件数量有限制。根据第二列的前3个字符中潜在字符组合的数量,此可能成为问题。这将影响任何解决方案,其中这些名称的文件在处理给定文件时保持打开 - 而不仅仅是awk。
如果您有000
到999
,则可能会打开999个潜在文件;如果你有AAA
到ZZZ
即17,575;如果您有三个带大写和小写的字母数字,那就是238,327 potential 打开文件...如果您的数据只有几个唯一的前缀,您可能不需要担心这个;如果您说明数据的详细信息,则此处建议的解决方案可能会有所不同。
(您可以根据3个字符中允许的字母长度计算基本转换为'ZZZ'
的潜在组合为小数。('0'..'9','A'..'Z')
基数为32 ('0'..'9','a'..'z','A'..'Z')
为基数62等等。)
如果需要(在合理范围内)或者根据需要打开和关闭新文件,您可以使用大多数Unix风格的操作系统提高限制。将文件限制提高到238,327是不切实际的。您还可以对数据进行排序,并在不使用时关闭以前的文件。