2009-12-17 97 views
3

我必须处理文本文件10-20GB格式的大小: 字段1场2场3字段4字段5如何快速解析大型文件(> 10GB)?

我想从FIELD2每一行数据解析成多个文件之一;这个被推入的文件是由field4中的值逐行确定的。 field2中有25个不同的可能值,因此数据可以分析到25个不同的文件中。我试过使用Perl(慢)和awk(更快但仍然很慢) - 有没有人有任何建议或指向其他方法?

仅供参考这里是我尝试使用的awk代码;注意:我不得不回到通过大文件25次去,因为我没能在awk将保持25个文件同时打开:

chromosomes=(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25) 
for chr in ${chromosomes[@]} 
do 

awk < my_in_file_here -v pat="$chr" '{if ($4 == pat) for (i = $2; i <= $2+52; i++) print i}' >> my_out_file_"$chr".query 

done 
+3

什么时候?可能提供一些您希望完成的范围?解析10GB的文本是一项相当大的工作。 – GrayWizardx 2009-12-17 02:01:00

+0

请在可能的位置显示示例文件 – ghostdog74 2009-12-17 03:03:30

回答

7

以下是Python中的解决方案。我已经在我制作的一个小假文件上进行了测试。我认为即使是一个大文件,这个速度也是可以接受的,因为大部分工作将由Python内部的C代码完成。我认为这是一个令人愉快且易于理解的程序;我更喜欢Python到Perl。

import sys 

s_usage = """\ 
Usage: csplit <filename> 
Splits input file by columns, writes column 2 to file based on chromosome from column 4.""" 

if len(sys.argv) != 2 or sys.argv[1] in ("-h", "--help", "/?"): 

    sys.stderr.write(s_usage + "\n") 
    sys.exit(1) 


# replace these with the actual patterns, of course 
lst_pat = [ 
    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 
    'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 
    'u', 'v', 'w', 'x', 'y' 
] 


d = {} 
for s_pat in lst_pat: 
    # build a dictionary mapping each pattern to an open output file 
    d[s_pat] = open("my_out_file_" + s_pat, "wt") 

if False: 
    # if the patterns are unsuitable for filenames (contain '*', '?', etc.) use this: 
    for i, s_pat in enumerate(lst_pat): 
     # build a dictionary mapping each pattern to an output file 
     d[s_pat] = open("my_out_file_" + str(i), "wt") 

for line in open(sys.argv[1]): 
    # split a line into words, and unpack into variables. 
    # use '_' for a variable name to indicate data we don't care about. 
    # s_data is the data we want, and s_pat is the pattern controlling the output 
    _, s_data, _, s_pat, _ = line.split() 
    # use s_pat to get to the file handle of the appropriate output file, and write data. 
    d[s_pat].write(s_data + "\n") 

# close all the output file handles. 
for key in d: 
    d[key].close() 

编辑:这里有一点关于这个程序的更多信息,因为它似乎你会使用它。

所有的错误处理都是隐含的。如果发生错误,Python将“引发异常”,从而终止处理。例如,如果其中一个文件无法打开,该程序将停止执行,Python将打印一个回溯,显示哪一行代码导致异常。我可以用“try/except”块封装关键部分来捕捉错误,但对于一个简单的程序,我没有看到任何意义。

这很微妙,但有一个检查,看看输入文件的每一行中是否只有五个单词。当这段代码解开一行时,它会将它变成五个变量。 (变量名称“_”是一个合法的变量名称,但Python社区有一个惯例将它用于你并不真正关心的变量。)如果Python中没有完全五个单词,则Python会引发异常输入行以解压缩为五个变量。如果你的输入文件有时可能有四个单词,或者六个或更多,你可以修改程序不会引发异常;将主循环更改为:

for line in open(sys.argv[1]): 
    lst = line.split() 
    d[lst[3]].write(lst[1] + "\n") 

这会将行拆分为单词,然后仅将整个单词列表分配到单个变量lst中。所以这一行代码并不关心有多少单词在线上。然后下一行索引到列表中以获取值。由于Python使用0索引列表以开始,因此第二个字是lst[1],第四个字是lst[3]。只要列表中至少有四个单词,该行代码也不会引发异常。

当然,如果行中的第四个单词不在文件句柄的字典中,Python也会引发异常。那会停止处理。下面是如何使用“try/except”块来处理这个问题的一些示例代码:

for line in open(sys.argv[1]): 
    lst = line.split() 
    try: 
     d[lst[3]].write(lst[1] + "\n") 
    except KeyError: 
     sys.stderr.write("Warning: illegal line seen: " + line) 

祝您的项目顺利。

编辑:@larelogio指出这段代码不符合AWK代码。 AWK代码有一个额外的循环,我不明白。这里是Python代码做同样的事情:

for line in open(sys.argv[1]): 
    lst = line.split() 
    n = int(lst[1]) 
    for i in range(n, n+53): 
     d[lst[3]].write(i + "\n") 

而这是另一种方式来做到这一点。这可能会快一点,但我没有测试过,所以我不确定。

for line in open(sys.argv[1]): 
    lst = line.split() 
    n = int(lst[1]) 
    s = "\n".join(str(i) for i in range(n, n+53)) 
    d[lst[3]].write(s + "\n") 

这建立一个单一的字符串与所有的数字写入,然后写入一个块。与调用.write() 53次相比,这可以节省时间。

+0

steveha - 非常感谢 - 我从来没有尝试过python,但是这个效果很好 - 我仍然看到〜两个小时左右来处理这个文件,这有点类似于awk的时间。我想我可能不得不冒险并按照约翰斯特罗姆的答案,即尝试C - 真的超出了我的专业知识,但我会给它一个圣诞节 1ch1g0 - 谢谢你 - 我走了因为我无法一次打开26个文件 - awk丢了一个错误;也许我应该尝试gawk或类似的。 节日快乐。 – Andrew 2009-12-21 16:44:51

+0

steveha - 更新 - 我说得太快了 - 你的python解决方案实际上运行了大约30分钟 - 这对我来说工作得很好,我非常感谢他的帮助。 – Andrew 2009-12-21 17:07:19

+0

我很高兴能够提供帮助。我用一些额外的信息更新了我的答案;我希望你能找到有用的附加信息。祝你的项目和节日快乐! – steveha 2009-12-21 19:41:57

7

你知道为什么它的慢?因为你正在用外壳循环处理那个大文件25次。

awk ' 
$4 <=25 { 
    for (i = $2; i <= $2+52; i++){ 
     print i >> "my_out_file_"$4".query" 
    } 
}' bigfile 
+0

您的输出文件规格中包含无与伦比的引用。 – 2009-12-17 02:37:25

+0

此外,它会增加每行的“pat”,因此在第26行后停止执行任何操作。您需要嵌套的for循环代替。 – 2009-12-17 02:46:21

+0

感谢捕捉。至于你的第二条评论,我会等待OP在做出改变之前显示他的输入文件。可以使用文件中表示下一次迭代的特定模式来增加pat变量。 – ghostdog74 2009-12-17 03:02:53

15

用Perl,初始化过程中打开文件,然后匹配输出的每一行到相应的文件:

#! /usr/bin/perl 

use warnings; 
use strict; 

my @values = (1..25); 

my %fh; 
foreach my $chr (@values) { 
    my $path = "my_out_file_$chr.query"; 
    open my $fh, ">", $path 
    or die "$0: open $path: $!"; 

    $fh{$chr} = $fh; 
} 

while (<>) { 
    chomp; 
    my($a,$b,$c,$d,$e) = split " ", $_, 5; 

    print { $fh{$d} } "$_\n" 
    for $b .. $b+52; 
} 
1

有些时候AWK是不是答案。

还有些时候脚本语言不是答案,当你咬下子弹并拖拽你的副本并剽窃一些C代码的时候就会变得更好。

如果您的操作系统使用并发进程和进程间通信实现管道,而不是大临时文件,那么您可以编写一个awk脚本来重新格式化该行,以将选择器字段置于以scanf()方式容易读取的格式的行,编写一个打开25个文件并在其中分布行的C程序,以及awk脚本输出到C程序的管道。

+2

-1:涉及大I/O时,C不起作用。他宁愿使用算法(例如一次读取文件,而不是25次),而不是切换语言! – Davide 2009-12-21 21:16:58

1

听起来像你在路上,但我只是想提到内存映射I/O作为一个巨大的帮助时处理巨大的文件。曾经有一段时间,我必须用Visual Basic 5解析一个.5GB的二进制文件(是的)...导入CreateFileMapping API允许我在分钟内解析该文件(并创建几个gig“人类可读”文件)。实施只需要半小时左右。

这里的描述在微软平台上的API的链接,但我敢肯定,MMIO应几乎任何平台上:MSDN

祝你好运!

0

有一些预先计算可能有帮助。

例如,您可以预先计算field2的每个值的输出。 Admiting,他们是25像字段4:

my %tx = map {my $tx=''; for my $tx1 ($_ .. $_+52) {$tx.="$tx1\n"}; $_=>$tx} (1..25); 

后的写入当你说你能不能详细慢,你可以做print {$fh{$pat}} $tx{$base};