如何快速解析大(> 10GB)文件?

And*_*rew 3 perl awk large-files

我必须处理格式为10-20GB的文本文件:field1 field2 field3 field4 field5

我想将每行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
Run Code Online (Sandbox Code Playgroud)

Gre*_*con 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;
}
Run Code Online (Sandbox Code Playgroud)


gho*_*g74 7

你知道为什么它慢吗?因为你用外壳for循环处理那个大文件25次.!!

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


ste*_*eha 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()
Run Code Online (Sandbox Code Playgroud)

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

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

它很微妙,但有一个检查,看输入文件的每一行是否正好有五个单词.当这段代码解包一行时,它会分为五个变量.(变量名称"_"是一个合法的变量名称,但是Python社区中有一个约定将它用于您实际上并不关心的变量.)如果不存在五个单词,Python将引发异常.输入行解压缩成五个变量.如果您的输入文件有时可以在一行上有四个单词,或者六行或更多,您可以修改程序以不引发异常; 将主循环更改为:

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

这会将行拆分为单词,然后将整个单词列表分配到单个变量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)
Run Code Online (Sandbox Code Playgroud)

祝你的项目好运.

编辑:@larelogio指出此代码与AWK代码不匹配.AWK代码有一个额外的for循环,我不明白.这是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")
Run Code Online (Sandbox Code Playgroud)

这是另一种方法.这可能会快一点,但我没有测试过,所以我不确定.

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")
Run Code Online (Sandbox Code Playgroud)

这将构建一个包含要写入的所有数字的字符串,然后将它们写入一个块中.与调用.write()53次相比,这可以节省时间.