【问题标题】:Perl on Hash and Array ReferencesPerl 关于哈希和数组引用
【发布时间】:2012-09-21 04:35:18
【问题描述】:

我有一个 csv 文件,transcation.csv,如下所示:

TransactionID,Date,AccNum,Type,Amt  
657520,02-07-1999,016901581432,Debit,16000  
657524,02-07-1999,016901581432,Debit,13000  
657538,09-07-1999,016901581432,Credit,11000  
657548,18-07-1999,016901581432,Credit,15500  
657519,02-07-1999,016901581433,Debit,12000  
657523,02-07-1999,016901581433,Credit,11000  
657529,03-07-1999,016901581433,Debit,15000  
657539,10-07-1999,016901581433,Credit,10000  
657541,11-07-1999,016901581434,Debit,12000  
657525,03-07-1999,016901581434,Debit,15000  
657533,05-07-1999,016901581434,Credit,12500  

我应该从transaction.csv 读取数据并将其存储在哈希中。然后将哈希和帐号列表传递给函数,然后使函数使用哈希中的数据计算列出的账户的总借方金额和贷方金额。

这是我的代码:

#!/usr/bin/perl

my %banks;
&arrhas_banks(\%banks);
my @accnum = qw(016901581432 016901581434);
hasarr_banks(\%banks,\@accnum);

sub arrhas_banks
{
  my $banks = shift;
  open(FH,"<transaction.csv") or die "Could not open 'transaction.csv' $!\n";
  while (my $data=<FH>) 
  {
    chomp($data);
    my @records = split ",", $data;
    $banks->{$records[0]} =
      { 'AccNum' => $records[2], 'Type' => $records[3], 'Amt' => $records[4] };
  }
  close(FH);
}

sub hasarr_banks {
  my ($banks, $accnum) = @_;
  foreach my $id (keys %{$banks}) {
    my $answer;
    foreach my $num (@{$accnum}) {
      if($banks->{$id}{'AccNum'} == $num) {
        $answer = 1;
      }
    }
    if($answer) {
      my $type = $banks->{$id}{'Type'};
      $total{$accnum}{$type} += $banks->{$id}{'Amt'};
    }
  }
  foreach my $no (@{$accnum}} { 
    print "Debit amount of $no is $total{$accnum}{'Debit'}";
    print "Credit amount of $no is $tot{$accnum}{'Credit'}";
  }
}

我应该产生这样的输出:

Debit amount of 016901581432 is 29000  
Credit amount of 016901581432 is 26500  
Debit amount of 016901581434 is 27000  
Credit amount of 016901581434 is 12500    

但我得到这样的输出:

Debit amount of 016901581432 is 56000    
Credit amount of 016901581432 is 39000  
Debit amount of 016901581434 is 56000  
Credit amount of 016901581434 is 39000  

我做错了什么?


#!/usr/bin/perl

my %banks;
&arrhas_banks(\%banks);
my @accnum = qw(016901581432 016901581434);
hasarr_banks(\%banks,\@accnum);

sub arrhas_banks
{
  my $banks = shift;
  open(FH,"<transaction.csv") or die "Could not open 'transaction.csv' $!\n";
  while (my $data=<FH>) 
  {
    chomp($data);
    my @records = split ",", $data;
    $banks->{$records[0]} =
      { 'AccNum' => $records[2], 'Type' => $records[3], 'Amt' => $records[4] };
  }
  close(FH);
}

sub hasarr_banks {
  my ($banks, $accnum) = @_;
  foreach my $id (keys %{$banks}) {
    my $answer;
    foreach my $num (@{$accnum}) {
      if($banks->{$id}{'AccNum'} == $num) {
        $answer = 1;
      }
    }
    if($answer) {
      my $type = $banks->{$id}{'Type'};
      $total{ $banks->{$id}{'AccNum'} }->{$type} += $banks->{$id}{'Amt'};
    }
  }
  foreach my $no (@{$accnum}) { 
    print "Debit amount of $no is $total{$accnum}{'Debit'}";
    print "Credit amount of $no is $total{$accnum}{'Credit'}";
  }
}                      

还没有输出。还是出错了?

【问题讨论】:

  • 您的某些代码有语法错误——例如,foreach my $no (@{$accnum}} { 应该是 foreach my $no (@{$accnum}) {——所以假设这不是您运行的确切代码是否公平?
  • @ruakh 他将accnum 声明为一个数组,所以不应该是foreach my $no (@accunm)吗?
  • @squiguy 不,他有一个全局的@accnum 和一个词法的$accnum. 大概他的意思是后者。
  • @TLP 没听懂,但你是对的。

标签: perl parsing csv


【解决方案1】:

您应该尝试使用 csv 模块,例如 Text::CSV。请注意,在下面的示例中,DATA 文件句柄可以替换为任何 IO 输入句柄。

use strict;
use warnings;
use Text::CSV;
use feature 'say';

my $c = Text::CSV->new( );                    # default settings are ok
$c->column_names( $c->getline(*DATA) );       # set headers
my %acc;

while (my $row = $c->getline_hr(*DATA)) {     # $row is a hash ref
    if ($row->{Type} eq "Debit") {
        $acc{ $row->{AccNum} }{Debit}  += $row->{Amt};
    } elsif ($row->{Type} eq "Credit") {
        $acc{ $row->{AccNum} }{Credit} += $row->{Amt};
    } else { warn "Bad csv line '@$row'" }    # precaution
}

for my $acc (keys %acc) {
    say "Debit amount of $acc is ", $acc{$acc}{Debit};
    say "Credit amount of $acc is ", $acc{$acc}{Credit};
}

__DATA__
TransactionID,Date,AccNum,Type,Amt
657520,02-07-1999,016901581432,Debit,16000  
657524,02-07-1999,016901581432,Debit,13000  
657538,09-07-1999,016901581432,Credit,11000  
657548,18-07-1999,016901581432,Credit,15500  
657519,02-07-1999,016901581433,Debit,12000  
657523,02-07-1999,016901581433,Credit,11000  
657529,03-07-1999,016901581433,Debit,15000  
657539,10-07-1999,016901581433,Credit,10000  
657541,11-07-1999,016901581434,Debit,12000  
657525,03-07-1999,016901581434,Debit,15000  
657533,05-07-1999,016901581434,Credit,12500  

输出:

Debit amount of 016901581433 is 27000
Credit amount of 016901581433 is 21000
Debit amount of 016901581434 is 27000
Credit amount of 016901581434 is 12500
Debit amount of 016901581432 is 29000
Credit amount of 016901581432 is 26500

【讨论】:

    【解决方案2】:

    你在重复计算。

    这段代码在两个地方被破坏:

    1. 首先,由于您的示例数据中的 AccNum 始终是 $accnum 列表中的 2 个帐户之一,因此 $answer 始终为 1。

      foreach 我的 $num (@{$accnum}) { if($banks->{$id}{'AccNum'} == $num) { $答案 = 1; } }

    2. 其次,您根据$accnum 键分配给$total - 但是,$accnum 是对帐户#s 列表的数组引用-看看您的子例程调用。

      换句话说,您的$total hashref 将只有一个键,看起来像“ARRAY(SOMETHING)”,而不是每个帐户# 的 2 个键。

    您需要做的只是分配到正确的帐户#:

    if ($answer) {
      my $type = $banks->{$id}{'Type'};
      $total{ $banks->{$id}{'AccNum'} }->{$type} += $banks->{$id}{'Amt'};
    }
    

    顺便说一句,就可读性而言,您的代码风格不是很好。不是我见过的最糟糕的,但绝对不接近理想。

    【讨论】:

    • 但是你能告诉我我应该在印刷部分做些什么改变吗?
    • 您需要打印$total{$no}{'Debit'}$total{$no}{'Credit'}
    • 我没有得到我在首先展示的脚本中所做的更改的输出
    猜你喜欢
    • 2016-06-06
    • 2014-01-04
    • 2011-07-26
    • 1970-01-01
    • 2017-02-24
    • 1970-01-01
    • 2011-12-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多