Perl在哈希中替换字符串

时间:2013-11-17 18:56:36

标签: regex perl

嗨,我有一个任意深度的perl哈希。我想用其他东西替换整个结构中的字符串。

这样做的正确方法是什么?

我做了类似的事

#convert the hash to string for manipulation
my $data = YAML::Dump(%hash);
# do manipulation
---
---
# time to get back the hash
%hash = YAML::Load($data);

2 个答案:

答案 0 :(得分:3)

您的想法对我来说似乎非常危险,因为很难确定替换不会破坏YAML::Dump输出中的某些内容,这会阻止结果再次被读回,或者更糟,这将改变散列结构,因为它在转储字符串中表示。如果您尝试执行的操作是将:替换为,将替换为',或者某种类似的东西,该怎么办?

我可能会做更像这样的事情:

use Scalar::Util 'reftype';

# replace $this with $that in key names and string values of $hash
#   recursively apply replacement in hash and all its subhashes
sub hash_replace {
  my ($hash, $this, $that) = @_;
  for my $k (keys %$hash) {
    # substitution in value
    my $v = $hash->{$k};
    if (ref $v && reftype($v) eq "HASH") {
       hash_replace($v, $this, $that);
    } elsif (! ref $v) {
       $v =~ s/$this/$that/og;
    }

  my $new_hash = {};
  for my $k (keys %$hash) {
    # substitution in key
    (my $new_key = $k) =~ s/$this/$that/og;
    $new_hash->{$new_key} = $hash->{$k};
  }
  %$hash = %$new_hash; # replace old keys with new keys
}

我在这里使用的s/…/…/替换可能不适合您的任务;你应该随意使用别的东西。例如,您可以传递两个函数$this$that,而不是字符串$key_change$val_change,它们分别应用于键和值,返回修改后的版本。请参阅以下######行:

use Scalar::Util 'reftype';

# replace $this with $that in key names and string values of $hash
#   recursively apply replacement in hash and all its subhashes
sub hash_replace {
  my ($hash, $key_change, $val_change) = @_;
  for my $k (keys %$hash) {

    # substitution in value
    my $v = $hash->{$k};
    if (ref $v && reftype($v) eq "HASH") {
       hash_replace($v, $key_change, $val_change);
    } elsif (! ref $v) {
       $v = $val_change->($v);                             #######
    }
  }

  my $new_hash = {};
  for my $k (keys %$hash) {
    # substitution in key
    my $new_key = $key_change->($k);                       #######
    $new_hash->{$new_key} = $hash->{$k};
  }
  %$hash = %$new_hash;
}

答案 1 :(得分:2)

这是攻击它的一种方法,通过哈希递归。在此代码中,您将传递sub,它可以对嵌套哈希中的每个值执行任何操作。此代码仅修改值,而不是键,并忽略嵌套结构中的其他引用类型(即标量引用,数组引用)。

#!/usr/bin/perl -w

use Modern::Perl;

## Visit all nodes in a nested hash.  Bare-bones.
sub visit_hash
{
    my ($start, $sub) = @_;
    my @q = ( $start );

    while (@q) {
        my $hash = pop @q;
        foreach my $key ( keys %{$hash} ) {
            my $ref = ref($hash->{$key});

            if ( $ref eq "" ) { # not a reference
                &$sub( $hash->{$key} );
                next;
            }

            if ( $ref eq "HASH" ) { # reference to a nested hash
                push @q, $hash->{$key};
                next;
            }

            # ignore other reference types.
        }
    }
}

以下是一个如何使用它的示例,在嵌套哈希中用e替换E

# Example of replacing a string in all values:
my %hash =
(
    a => "fred",
    b => "barney",
    c => "wilma",
    d => "betty",

    nest1 =>
    {
        1 => "red",
        2 => "orange",
        3 => "green"
    },

    nest2 =>
    {
        x => "alpha",
        y => "beta",
        z => "gamma"
    },
);


use YAML::XS;

print "Before:\n";
print Dump( \%hash );

# now replace 'e' with 'E' in all values.
visit_hash( \%hash, sub { $_[0] =~ s/e/E/g; } );

print "After:\n";
print Dump( \%hash );