我的perl脚本中有以下命令:
my @files = `find $basedir/ -type f -iname '$sampleid*.summary.csv'`; #there are multiple summary.csv files in my basedir. I store them in an array
my $summary = `tail -n 1 $files[0]`; #Each summary.csv contains a header line and a line with data. I fetch here the last line.
chomp($summary);
my @sp = split(/,/,$summary); # I split based on ','
my $gender = $sp[11]; # the values from column 11 are stored in $gender
my $qc = $sp[2]; # the values from column 2 are stored in $gender
现在,我遇到的情况是我的* summary.csv文件没有相同的列数。它们都有2行,第一行代表标题。
我现在想要的不是将第11列中的值存储在性别中,但我希望将“性别”列中的值存储在$ gender中。
我怎样才能做到这一点?
首先尝试解决方案:
my %hash = ();
my $header = `head -n 1 $files[0]`; #reading the header
chomp ($header);
my @colnames = split (/,/,$header);
my $keyfield = $colnames[#here should be the column with the name 'Gender']
push @{ $hash{$keyfield} };
my $gender = $sp[$keyfield]
答案 0 :(得分:3)
您必须阅读标题行以及数据以了解哪些列包含哪些信息。这通过编写实际的Perl代码而不是shelling到各种命令行实用程序来完成。请参阅下面的解决方案。
修复解决方案也需要哈希。您需要首先读取标题行,将标题字段存储在数组中(如您所做),然后读取数据行。数据需要是哈希,而不是数组。哈希是键和值的映射。
# read the header and create a list of header fields
my $header = `head -n 1 $files[0]`;
chomp ($header);
my @colnames = split (/,/,$header);
# read the data line
my $summary = `tail -n 1 $files[0]`;
chomp($summary);
my %sp; # use a hash for the data, not an array
# use a hash slice to fill in the columns
@sp{@colnames} = split(/,/,$summary);
my $gender = $sp{Gender};
这里棘手的部分是这一行。
@sp{@colnames} = split(/,/,$summary);
我们已将%sp
声明为哈希,但我们现在使用@
sigil访问它。这是因为我们正在使用a hash slice,如花括号{}
所示。我们采用的切片是@colnames
中具有值名称的所有元素。有多个值,因此返回值不再是标量(带有$
)。有一个返回值列表,因此sigil变为@
。现在我们在左侧使用该列表(称为LVALUE),并将split
的结果分配给该列表。
以下程序将使用File :: Find :: Rule替换find
命令,使用Text :: CSV读取CSV文件。它抓取所有文件,然后一次打开一个。首先读取标题行,然后将其输入Text :: CSV对象,以便它可以返回一个哈希引用,您可以使用该引用按名称访问每个字段。
我写的方式是每个文件只读一行,正如你所说的那样,每个文件只有两行。您可以轻松地将其扩展为循环。
use strict;
use warnings;
use File::Find::Rule;
use Text::CSV;
my $sampleid;
my $basedir;
my $csv = Text::CSV->new(
{
binary => 1,
sep => ',',
}
) or die "Cannot use CSV: " . Text::CSV->error_diag;
my @files = File::Find::Rule->file()->name("$sampleid*.summary.csv")->in($basedir);
foreach my $file (@files) {
open my $fh, '<', $file or die "Can't open $file: $!";
# get the headers
my @cols = @{ $csv->getline($fh) };
$csv->column_names(@cols);
# read the first line
my $row = $csv->getline_hr($fh);
# do whatever you you want with the row
print "$file: ", $row->{gender};
}
请注意,我没有测试过这个程序。