【问题标题】:How to parse through tab-delimited file in perl?如何在 perl 中解析制表符分隔的文件?
【发布时间】:2012-10-24 08:04:58
【问题描述】:

我是 Perl 新手,但我遇到了心理障碍。我需要从制表符分隔的文件中提取信息,如下所示。

#name  years risk total
 adam  5     100  200
 adam  5     50   100
 adam  10    20   300
 bill  20    5    100
 bill  30    10   800

在此示例中,制表符分隔的文件显示投资期限、风险金额和投资结束时的总额。

我想解析这个文件,对于每个名字(例如adam),计算投资年数总和5+5,并计算收入总和(200-100) + (100-50) + (300-20)。我还想保存每个名称的总数(200、100、300)。

这是我迄今为止尝试过的:

my $filename;
my $seq_fh;

open $seq_fh, $frhitoutput 
    or die "failed to read input file: $!";

while (my $line = <$seq_fh>) {

    chomp $line;
    ## skip comments and blank lines and optional repeat of title line

    next if $line =~ /^\#/ || $line =~ /^\s*$/ || $line =~ /^\+/;

    #split each line into array
    my @line = split(/\s+/, $line);
    my $yeartotal = 0;
    my $earning   = 0;

    #$line[0] = name
    #$line[1] = years
    #$line[2] = start
    #$line[3] = end

    while (@line[0]){

        $yeartotal += $line[1];
        $earning   += ($line[3]-$line[2]);
    }
}

对我哪里出错有任何想法吗?

【问题讨论】:

  • 如果它真的是制表符分隔的,你应该在/\t/而不是/\s+/上分割。否则,您将无法处理空字段或其中包含空格的字段。

标签: perl parsing


【解决方案1】:

Text::CSV 模块可用于读取制表符分隔的数据。在引用、转义等方面,通常比尝试使用split 等手动破解自己的东西要好得多。

【讨论】:

    【解决方案2】:

    你错了:while(@line[0]){

    我愿意:

    my $seq_fh;
    my %result;
    open($seq_fh, $frhitoutput) || die "failed to read input file: $!";
    while (my $line = <$seq_fh>) {
        chomp $line;
        ## skip comments and blank lines and optional repeat of title line
        next if $line =~ /^\#/ || $line =~ /^\s*$/ || $line =~ /^\+/;
        #split each line into array
        my @line = split(/\s+/, $line);
        $result{$line[0]}{yeartotal} += $line[1];
        $result{$line[0]}{earning} += $line[3] - $line[2];
    }
    

    【讨论】:

    • 太棒了!完美运行。如果我要保存总数(200、100、300),我会怎么做呢?哈希可以有另一个维度吗?
    【解决方案3】:

    你应该使用哈希,像这样:

    my %hash;
    while (my $line = <>) {
    
        next if $line =~ /^#/;
    
        my ($name, $years, $risk, $total) = split /\s+/, $line;
    
        next unless defined $name and defined $years
                and defined $risk and defined $total;
    
        $hash{$name}{years}    += $years;
        $hash{$name}{risk}     += $risk;
        $hash{$name}{total}    += $total;
        $hash{$name}{earnings} += $total - $risk;
    }
    
    foreach my $name (sort keys %hash) {
    
        print "$name earned $hash{$name}{earnings} in $hash{$name}{years}\n";
    }
    

    【讨论】:

    • 太棒了!完美运行。如果我要保存个人总数(200、100、300),我会怎么做呢?哈希可以有另一个维度吗?
    • 哈希可以达到你想要的深度。在这种情况下,我将其设置为 2 级深度:名称,然后是一些数据,如年份、总数等。但是,如果您想保存单个记录,则需要拥有使该记录独一无二的东西——这就是哈希的意义所在。
    • 我必须在哈希中创建一个哈希吗?
    • 实际上,您在这里隐含地处理哈希引用。它是一个包含哈希引用作为值的哈希。
    • 我很难过。显然你不能在哈希引用中存储唯一值。
    【解决方案4】:

    探索 Perl 强大的命令行选项的好机会! :)

    代码

    注意:这段代码应该是一个命令行单行代码,但是这样读起来更容易一些。在适当的脚本文件中编写它时,您确实应该启用 strict and warnings 并使用更好的名称。此版本无法在 strict 下编译,您必须声明our $d

    #!/usr/bin/perl -nal
    
    # collect data
    $d{$F[0]}{y} += $F[1];
    $d{$F[0]}{e} += $F[3] - $F[2];
    
    # print summary
    END { print "$_:\tyears: $d{$_}{y},\tearnings: $d{$_}{e}" for sort keys %d }
    

    输出

    adam:   years: 20,  earnings: 430
    bill:   years: 50,  earnings: 885
    

    说明

    我在这里使用-n 开关,它基本上可以让您的代码遍历输入记录(-l 告诉它使用行)。 -a 开关让 perl 将行拆分为数组 @F。简化版:

    while (defined($_ = <STDIN>)) {
        chomp $_;
        our(@F) = split(' ', $_, 0);
    
        # collect data
        $d{$F[0]}{y} += $F[1];
        $d{$F[0]}{e} += $F[3] - $F[2];
    }
    

    %d 是一个哈希值,名称为键,hashrefs 为值,包含年份 (y) 和收入 (e)。

    END块在完成输入行处理后执行,输出%d

    使用ODeparse查看实际执行的代码:

    book:/tmp memowe$ perl -MO=Deparse tsv.pl
    BEGIN { $/ = "\n"; $\ = "\n"; }
    LINE: while (defined($_ = <ARGV>)) {
        chomp $_;
        our(@F) = split(' ', $_, 0);
        $d{$F[0]}{'y'} += $F[1];
        $d{$F[0]}{'e'} += $F[3] - $F[2];
        sub END {
            print "${_}:\tyears: $d{$_}{'y'},\tearnings: $d{$_}{'e'}" foreach (sort keys %d);
        }
        ;
    }
    tsv.pl syntax OK
    

    【讨论】:

      【解决方案5】:

      这似乎是一个固定宽度的文件,我会使用 unpack 来做这个

      【讨论】:

        猜你喜欢
        • 2013-10-16
        • 2013-10-20
        • 2011-06-02
        • 1970-01-01
        • 2015-02-14
        • 2012-06-19
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多