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