perl - 从文件中获取列名

时间:2017-08-07 08:55:59

标签: perl

我的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]

1 个答案:

答案 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的结果分配给该列表。

使用现代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};
}

请注意,我没有测试过这个程序。