用perl解析一个巨大的文本文件

用perl解析一个巨大的文本文件,perl,Perl,我有一个以制表符分隔的文本文件。它们可以相当大,高达1GB。我将有可变的列数取决于其中的样本数。每个样本有八列。例如,样本:ID1、id2、MIN_A、AVG_A、MAX_A、AR1_A、AR2_A、AR_A、AR_A、AR_5。其中ID1和id2是所有样本的公用项。我想要实现的是根据样本数量将整个文件分割成文件块 ID1,ID2,MIN_A,AVG_A,MAX_A,AR1_A,AR2_A,AR3_A,AR4_A,AR5_A,MIN_B, AVG_B, MAX_B,AR1_B,AR2_B,AR3

我有一个以制表符分隔的文本文件。它们可以相当大,高达1GB。我将有可变的列数取决于其中的样本数。每个样本有八列。例如,样本:ID1、id2、MIN_A、AVG_A、MAX_A、AR1_A、AR2_A、AR_A、AR_A、AR_5。其中ID1和id2是所有样本的公用项。我想要实现的是根据样本数量将整个文件分割成文件块

ID1,ID2,MIN_A,AVG_A,MAX_A,AR1_A,AR2_A,AR3_A,AR4_A,AR5_A,MIN_B, AVG_B, MAX_B,AR1_B,AR2_B,AR3_B,AR4_B,AR5_B,MIN_C,AVG_C,MAX_C,AR1_C,AR2_C,AR3_C,AR4_C,AR5_C
12,134,3535,4545,5656,5656,7675,67567,57758,875,8678,578,57856785,85587,574,56745,567356,675489,573586,5867,576384,75486,587345,34573,45485,5447
454385,3457,485784,5673489,5658,567845,575867,45785,7568,43853,457328,3457385,567438,5678934,56845,567348,58567,548948,58649,5839,546847,458274,758345,4572384,4758475,47487
这是我的模型文件的外观,我希望它们是:

File A : 
ID1,ID2,MIN_A,AVG_A,MAX_A,AR1_A,AR2_A,AR3_A,AR4_A,AR5_A
12,134,3535,4545,5656,5656,7675,67567,57758,875
454385,3457,485784,5673489,5658,567845,575867,45785,7568,43853

File B:
ID1, ID2,MIN_B, AVG_B, MAX_B,AR1_B,AR2_B,AR3_B,AR4_B,AR5_B
12,134,8678,578,57856785,85587,574,56745,567356,675489
454385,3457,457328,3457385,567438,5678934,56845,567348,58567,548948

File C:

ID1, ID2,MIN_C,AVG_C,MAX_C,AR1_C,AR2_C,AR3_C,AR4_C,AR5_C
12,134,573586,5867,576384,75486,587345,34573,45485,5447
454385,3457,58649,5839,546847,458274,758345,4572384,4758475,47487.
有没有比彻底检查阵列更简单的方法

我是如何计算出我的逻辑的(头的数量-2)并将它们除以8将得到文件中的样本数量。然后遍历数组中的每个元素并解析它们。这似乎是一种乏味的方式。我很乐意知道更简单的处理方法

谢谢 Sipra

#/bin/env perl
严格使用;
使用警告;
#打开三个输出文件句柄
我的%fh;
对于(qw[abc]){
打开$fh{$},'>',“文件$”或die$!;
}
#开放输入
在“{fh}}join(“,”,@data[0,1]”中打开我的$,
@数据[$f->{start\u col}..$f->{end\u col}]),
“\n”;
}
}

您说过制表符分隔,但您的示例显示它是逗号分隔的。我认为这是一个限制,把你的样本数据降价

我猜你有点担心内存,所以你想打开多个文件并在解析大文件时写入它们

我想试试看。但是,我相信它会将整个文件读入内存,这对于这样大小的文件可能是个问题

读一行并把它放到列表中是很容易的。问题是将该列表中的字段映射到字段本身的名称

如果使用
while
循环读入一个文件,则不会立即将整个文件读入内存。如果您读入每一行,解析该行,然后将该行写入各种输出文件,那么就不会占用大量内存。有一个缓存,但我相信在将
\n
写入文件后它会被清空

诀窍是打开输入文件,然后读入第一行。您需要创建某种字段映射结构,以便确定要写入每个输出文件的字段

我会有一个你需要写入的所有文件的列表。这样,您可以浏览每个文件的列表。列表中的每个项目都应包含写入该文件所需的信息

首先,您需要一个文件句柄,以便知道要写入哪个文件。其次,您需要一个字段号列表,您必须将其写入特定的输出文件

我看到一些类似这样的处理循环:

while (my $line = <$input_fh>) {   #Line from the input file.
   chomp $line;
   my @input_line_array = split /\t/, $line;
   my $fileHandle;
   foreach my $output_file (@outputFileList) {  #List of output files.
       $fileHandle = $output_file->{FILE_HANDLE};
       my @fieldsToWrite;
       foreach my $fieldNumber (@{$output_file->{FIELD_LIST}}) {
          push $fieldsToWrite, $input_line_array[$field];
       }
       say $file_handle join "\t", @fieldsToWrite;
   }
}
表示我要将以下字段写入输出文件:
$input\u line\u array[0]
$input\u line\u array[1]
$input\u line\u array[2]
$input\u line\u array[4]
$input\u line\u array[6]
,以及
$input\u line\u array[8]
写入我的输出文件
$outputFileList->{FILE_HANDLE}
按该顺序作为制表符分隔的列表

我希望这是有意义的

最初的问题是读取
的第一行并将其解析为所需的复杂结构。但是,现在您已经了解了该结构需要如何存储,解析第一行应该不是什么大问题


虽然我在这个例子中没有使用面向对象的代码(我在写这篇文章的时候把这些东西从我的大脑里拿出来)。我肯定会使用面向对象的代码方法。它实际上会通过删除错误来加快速度。

这里有一行代码来打印第一个示例,您可以编写一个shell脚本,将不同示例的数据写入不同的文件

perl -F, -lane 'print "@F[0..1] @F[2..9]"' <INPUT_FILE_NAME>
perl-F,-lane'print“@F[0..1]@F[2..9]”

这与样本数量无关。但我对输出文件名没有信心,因为可能会有超过26个样本。如果是这样,只需替换输出文件名的工作方式即可。:)


从技术上讲,任何事情都是可能的。但是你对输入和输出格式的描述太模糊了。你需要更精确地定义它们。“我有一个以制表符分隔的文本文件。”你说,但是你的示例输入是以逗号分隔的。我感觉可能有很多“集合”一行上有很多数据,他只用了三行作为例子。对。因此我的扩展版本。大家好,非常感谢,我学到了很多这样做的方法。再次感谢。
while (my $line = <$input_fh>) {   #Line from the input file.
   chomp $line;
   my @input_line_array = split /\t/, $line;
   my $fileHandle;
   foreach my $output_file (@outputFileList) {  #List of output files.
       $fileHandle = $output_file->{FILE_HANDLE};
       my @fieldsToWrite;
       foreach my $fieldNumber (@{$output_file->{FIELD_LIST}}) {
          push $fieldsToWrite, $input_line_array[$field];
       }
       say $file_handle join "\t", @fieldsToWrite;
   }
}
$outputFileList[$fileNumber]->{FIELD_LIST} = [0, 1, 2, 4, 6, 8];
perl -F, -lane 'print "@F[0..1] @F[2..9]"' <INPUT_FILE_NAME>
use strict;
use warnings;

use File::Slurp;
use Text::CSV_XS;
use Carp qw( croak );

#I'm lazy
my @source_file = read_file('source_file.csv');
# you metion yours is tab separated
# just add the {sep_char => "\t"} inside new
my $csv = Text::CSV_XS->new()
  or croak "Cannot use CSV: " . Text::CSV_XS->error_diag();
my $output_file;

#read each row
while ( my $raw_line = shift @source_file ) {
    $csv->parse($raw_line);
    my @fields = $csv->fields();

    #get the first 2 ids
    my @ids = splice @fields, 0, 2;

    my $group = 0;
    while (@fields) {
        #get the first 8 columns
        my @columns = splice @fields, 0, 8;
        #if you want to change the separator of the output replace ',' with "\t"
        push @{ $output_file->[$group] }, (join ',', @ids, @columns), $/;
        $group++;
    }
}

#for filename purposes
my $letter = 65;
foreach my $data (@$output_file) {
    my $output_filename = sprintf( 'SAMPLE_%c.csv', $letter );
    write_file( $output_filename, @$data );
    $letter++;
}

#if you reach more than 26 samples then you might want to use numbers instead
#my $sample_number = 1;
#foreach my $data (@$output_file) {
#    my $output_filename = sprintf( 'sample_%s.csv', $sample_number );
#    write_file( $output_filename, @$data );
#    $sample_number++;
#}