【问题标题】:Perl Hashref SubstitutionsPerl Hashref 替换
【发布时间】:2012-01-25 16:49:34
【问题描述】:

我正在使用 DBI 连接到 Sybase 以获取 hash_ref 元素中的记录。 DBI::Sybase 驱动程序有一个讨厌的习惯,即返回带有尾随字符的记录,特别是在我的例子中是 \x00。我正在尝试编写一个函数来清理 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;
        }

【问题讨论】:

  • 你为什么试图通过它的值来访问一个哈希键? $qry->{$val} 应该给你警告Use of uninitialized value in substitution。除非 a) 您没有使用警告,或者 b) 您碰巧拥有与所有键相同的值。
  • 我不禁觉得这是不应该用正则表达式修补的东西,但要正确修复。

标签: perl hash


【解决方案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;
        }
    }
}

【讨论】:

    【解决方案2】:

    首先你的代码:

       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 %$_ : $_
    }
    

    【讨论】:

      猜你喜欢
      • 2012-06-07
      • 2012-09-20
      • 2015-01-19
      • 1970-01-01
      • 1970-01-01
      • 2014-01-17
      • 2011-01-23
      • 1970-01-01
      • 2011-02-15
      相关资源
      最近更新 更多