我的 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]
最佳答案
您必须阅读标题行和数据才能知道哪列包含哪些信息。最简单的方法是编写实际的 Perl 代码,而不是使用各种命令行实用程序。有关该解决方案,请参阅下文。
修复您的解决方案还需要哈希值。您需要首先读取标题行,将标题字段存储在数组中(就像您已经完成的那样),然后读取数据行。数据需要是散列,而不是数组。哈希是键和值的映射。
# 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
中值的名称的所有元素。有多个值,因此返回值不再是标量(带有 $
)。有一个返回值列表,因此印记变为 @
。现在我们使用左侧的列表(称为 LVALUE ),并将 split
的结果分配给该列表。
使用现代 Perl 来实现
以下程序将使用 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};
}
请注意,我尚未测试过该程序。
关于perl - 从文件中获取列名,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45542989/