Regex Perl在哈希中替换字符串

Regex Perl在哈希中替换字符串,regex,perl,Regex,Perl,嗨,我有一个任意深度的perl哈希。我想用其他东西替换整个结构中的a字符串 正确的方法是什么 我做了这样的事 #convert the hash to string for manipulation my $data = YAML::Dump(%hash); # do manipulation --- --- # time to get back the hash %hash = YAML::Load($data); 您的想法对我来说似乎非常危险,因为很难确保替换不会破坏YAML::Dump输

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

正确的方法是什么

我做了这样的事

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

您的想法对我来说似乎非常危险,因为很难确保替换不会破坏
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\u change
$val\u 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;
}

您的想法对我来说似乎非常危险,因为很难确保替换不会破坏
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\u change
$val\u 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;
}

这里有一种攻击它的方法,通过哈希递归。在这段代码中,您传入一个
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 );

这里有一种攻击它的方法,通过哈希递归。在这段代码中,您传入一个
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 );

键、值或两者?键、值或两者?
标量
while
循环的条件下是多余的,因为条件始终是标量。@MJD:True。不幸的是,我的习惯是包含它,因为没有它我的其他程序员不太清楚。我不明白
while(scalar@q)
如何比
while(@q)
更清楚,因为前者需要理解晦涩且很少使用的
scalar
操作符,但是好的。@MJD:我读过你的高阶Perl书籍。你比我的同事更精通perl::-)我们大多是EEs,而不是CS。我曾经想出一个理论,认为
$#array
被过度使用,当它被使用时,通常是多余的或错误的。我做了一项研究,查看了Usenet上示例中发布的数百个
$#array
的用法,发现我的理论是不正确的:
$#array
在大约20%的时间里是冗余的或错误的。
scalar
while
循环的情况下是冗余的,因为一个条件总是一个标量。@MJD:True。不幸的是,我的习惯是包含它,因为没有它我的其他程序员不太清楚。我不明白
while(scalar@q)
如何比
while(@q)
更清楚,因为前者需要理解晦涩且很少使用的
scalar
操作符,但是好的。@MJD:我读过你的高阶Perl书籍。你比我的同事更精通perl::-)我们大多是EEs,而不是CS。我曾经想出一个理论,认为
$#array
被过度使用,当它被使用时,通常是多余的或错误的。我做了一项研究,查看了Usenet上示例中发布的数百个
$#array
,发现我的理论是不正确的:
$#array
在大约20%的时间里是多余的或错误的。当然,这会在开始时快照一组键,但这行在做什么,它如何不依赖于访问顺序<代码>$hash->{$new_key}删除$hash->{$k}
例如,如果我有
(a=>1,b=>2)
,并且我的
$key\u change
将a映射到b,b映射到c,那么
$hash->{b}delete$hash->{a}
在我访问它之前,如果
键碰巧返回
('a',b')
,那么
的操作将对
进行clobbers
操作。如果我的映射函数将a映射到b,b映射到a,那么没有访问顺序是安全的。当然,这会在开始时快照一组键,但是此行的作用是什么,它如何不依赖于访问顺序<代码>$hash->{$new_key}删除$hash->{$k}
例如,如果我有
(a=>1,b=>2)
,并且我的
$key\u change
将a映射到b,b映射到c,那么
$hash->{b}delete$hash->{a}
在我访问它之前,如果
键碰巧返回
('a',b')
,那么
的操作将对
进行clobbers
操作。如果我的映射函数映射