2012-01-25 59 views
0

我使用DBI连接到Sybase以抓取hash_ref元素中的记录。 DBI :: Sybase驱动程序有一个令人讨厌的习惯,就是返回包含尾随字符的记录,在我的情况下特别是\ x00。我试图编写一个函数来清理hashref中的所有元素,我在下面的代码中做了这个窍门,但是我找不到一个方法来使它更加精简,而且我知道有这么做是为了做到这一点好:Perl Hashref替换

#!/usr/bin/perl 

my $dbh = DBI->connect('dbi:Sybase:...'); 

my $sql = qq {SELECT * FROM table WHERE age > 18;}; 
my $qry = $dbh->selectall_hashref($sql, 'Name'); 

     foreach my $val(values %$qry) { 
       $qry->{$val} =~ s/\x00//g; 
     } 
     foreach my $key(keys %$qry) { 
       $qry->{$key} =~ s/\x00//g; 
       foreach my $val1(keys %{$qry->{$key}}) { 
         $qry->{$key}->{$val1} =~ s/\x00//g; 
       } 
       foreach my $key1(keys %{$qry->{$key}}) { 
         $qry->{$key}->{$key1} =~ s/\x00//g; 
     } 
+0

你为什么试图通过它的值访问散列键? '$ qry - > {$ val}'应该给你警告'在替换中使用未初始化的值'。除非a)您没有使用警告,或者b)您碰巧拥有与所有按键相同的值。 – TLP

+0

我不禁觉得这是不应该用正则表达式修补的东西,但正确修复。 – TLP

回答

1

尽管我认为,正则表达式替换不完全是一种理想的解决方案(好像它应该被妥善固定相反),这里有一个方便的方法来解决它与chomp

use Data::Dumper; 

my %a = (
    foo => { 
     a => "foo\x00", 
     b => "foo\x00" 
    }, 
    bar => { 
     c => "foo\x00", 
     d => "foo\x00" 
    }, 
    baz => { 
     a => "foo\x00", 
     a => "foo\x00" 
    } 
); 
$Data::Dumper::Useqq=1; 
print Dumper \%a; 
{ 
    local $/ = "\x00"; 
    chomp %$_ for values %a; 
} 
print Dumper \%a; 

chomp将删除单个拖尾值等于任何输入记录分隔符$/被设定为。在散列上使用时,它会将值压碎。我们不需要直接使用这些值,因为它们是别名。还要注意使用local $/语句附近的块来限制其范围。

对于更易于管理的解决方案,最好制作一个递归调用的子例程。我在这里再次使用chomp,但您可以轻松跳过并使用s/\x00//g。或者说tr/\x00//d,它们基本上是一样的。 chomp只是比较安全,因为它只从字符串的末尾删除字符,如s/\x00$//会。

strip_null(\%a); 
print Dumper \%a; 

sub strip_null { 
    local $/ = "\x00"; 
    my $ref = shift; 
    for (values %$ref) { 
     if (ref eq 'HASH') { 
      strip_null($_); # recursive strip 
     } else { 
      chomp; 
     } 
    } 
} 
1

首先代码:

foreach my $val(values %$qry) { 
      $qry->{$val} =~ s/\x00//g; 
      # here you are using a value as if it was a key 
    } 
    foreach my $key(keys %$qry) { 
      $qry->{$key} =~ s/\x00//g; 
      foreach my $val1(keys %{$qry->{$key}}) { 
        $qry->{$key}->{$val1} =~ s/\x00//g; 
      } 

      foreach my $key1(keys %{$qry->{$key}}) { 
        $qry->{$key}->{$key1} =~ s/\x00//g; 
    } 
      # and this does the same thing twice... 

你应该做的是:

foreach my $x (values %$qry) { 
    foreach my $y (ref $x eq 'HASH' ? values %$x : $x) { 
     $y =~ s/(?:\x00)+$// 
    } 
} 

将在哈希的两个层次的价值观清理仅仅结束空。

的循环体也可以写为:

if (ref $x eq 'HASH') { 
     foreach my $y (values %$x) { 
      $y =~ s/(?:\x00)+$// 
     } 
    } 
    else { 
     $x =~ s/(?:\x00)+$// 
    } 

但是,这迫使你写的替代两次,你不应该重复自己。

或者,如果你真的想减少代码,使用隐式$_可变效果很好:

for (values %$qry) { 
    s/(?:\x00)+$// for ref eq 'HASH' ? values %$_ : $_ 
}