2012-01-25 96 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 %$_ : $_ 
}