perl代码如下所示:问题是我无法在sub tweak_server {}内部读取$ key ....
my $key;
my %hash = ( flintstones => [ "C:/Users1/f1.xml", "C:/Users1/f2.xml" ],
jetsons => [ "C:/Users2/f1.xml" ],
simpsons => [ "C:/Users3/f1.xml", "C:/Users3/f1.xml", "C:/Users3/f1.xml" ], );
foreach $key (keys%hash){
if (scalar@{$hash{$key}}>1){
foreach my $path (@{$hash{$key}}){
my $filehandle;
open($filehandle, "+<$path") or die "cannot open out file out_file:$!";
my $roots = { TAG => 1 };
my $handlers = { 'ROOT/TAG' => \&tweak_server,
};
my $twig = new XML::Twig(TwigRoots => $roots,
TwigHandlers => $handlers,
twig_print_outside_roots => \*$filehandle);
$twig->parsefile($path);
say $key;#could read key
sub tweak_server {
my ($twig, $root) = @_;
my $tag2=$root->first_child_text('TAG2');
say $key;# could not read
if ($tag2=~/$key/){
#BLABLA
}
$twig->flush( $filehandle, pretty_print => 'indented');
}
}
}
}
正如我所说的那样,$ key可以在sub之外读取,但不能在内部..错误出现:使用未初始化的值$ key
然后我尝试了一个简单的情况,就像
一样my $a="aaa";
open( $filehandle, "+<$path") or die "cannot open out file out_file:$!";
my $roots = { TAG => 1 };
my $handlers = { 'ROOT/TAG' => \&tweak_server,
};
my $twig = new XML::Twig(TwigRoots => $roots,
TwigHandlers => $handlers,
twig_print_outside_roots => \*$filehandle
);
$twig->parsefile($path);
sub tweak_server {
say $a;
my ($twig, $root) = @_;
my $tags=$root->first_child_text('TAG2');
my $str="204B";
if ($tag2=~m/$str/){
foreach my $b(1...6){
say $a; }
}
$twig->flush( $filehandle, pretty_print => 'indented');
}
在此代码中,$ a可以读取.... 我花了一天时间,但仍然无法解决它...现在疯了 提前谢谢!!
答案 0 :(得分:4)
您在$key
循环之外声明for
。然后,在for循环中,定义一个在$key
上关闭的子例程。
作为一般规则,在最小适用范围内声明变量。例如:
for my $key (keys ...) {
或
open my $filehandle, '<', ...
为什么要在sub tweak_server
循环体中定义for
?在我看来,你想要做的是为每个迭代定义一个新的匿名子。
首先,一个复制您正在观察的内容的简短示例:
use warnings; use strict;
my $key;
my %hash = qw(a b c d e f);
foreach $key (keys %hash) {
somesub();
sub somesub {
print "$key\n";
}
}
现在,修复:
use warnings; use strict;
my %hash = qw(a b c d e f);
foreach my $key (keys %hash) {
my $somesub = sub { print "$key\n" };
$somesub->();
}
这样,我们在每次迭代时定义一个新的匿名函数,每个新子函数关闭循环变量的每个值。
就代码而言,您应该用
替换命名的子代码my $tweak_server = sub {
my ($twig, $root) = @_;
my $tag2=$root->first_child_text('TAG2');
say $key;# could not read
if ($tag2=~/$key/){
#BLABLA
}
$twig->flush( $filehandle, pretty_print => 'indented');
}
my $handlers = {
'ROOT/TAG' => $tweak_server,
};
或者,甚至更好,正如@mirod观察到的那样,将$key
传递给tweak_server
:
sub tweak_server {
my( $key, $twig, $root)= @_;
...
}
而且,在循环体中,
my $handlers = {
'ROOT/TAG' => sub { tweak_server($key, @_) },
};
答案 1 :(得分:4)
简短版本:如果你有一个命名子声明(sub foo { ... }
)在一个循环的另一个子或内部,你可能做错了,你可能想要一个匿名子。这就是这种情况。
sub foo { ... }
与
基本相同BEGIN { *foo = sub { ... }; }
如果sub引用了自身之外的任何词法(my
)变量,它们将在执行sub
时被捕获,这是在编译时使用命名的subs。一个常见的错误就是
sub outer {
my ($x) = @_;
sub inner {
... $x ...
}
outer();
}
这不起作用,因为多次调用my $x
来创建多个变量,但inner
仅捕获编译时存在的变量。 (幸运的是,它警告说。)
乍一看,您的代码只有一个名为$key
的变量,因此在编译时捕获它应该没有问题。但他们看起来很欺骗。由于foreach的别名功能,迭代器变量不是它在循环外部的变量。
$ perl -E'
my $x; say "x: ", 0+\$x; # Show address of variable.
my $y; say "y: ", 0+\$y;
my $z; say "z: ", 0+\$z;
for $x ($y, $z) { say "i: ", 0+\$x; }
'
x: 155771632
y: 155771584
z: 155771744
i: 155771584
i: 155771744
在你的情况下,你有
$ perl -E'
my $x; say "x: ", 0+\$x;
my $y; say "y: ", 0+\$y;
my $z; say "z: ", 0+\$z;
for $x ($y, $z) {
say "i: ", 0+\$x;
sub f { say "f: ", 0+\$x; }
f();
}
'
x: 142992144
y: 142992096
z: 142992256
i: 142992096
f: 142992144
i: 142992256
f: 142992144
您希望sub在运行时捕获$x
,并且这是通过使用匿名子来完成的。
$ perl -E'
my $x; say "x: ", 0+\$x;
my $y; say "y: ", 0+\$y;
my $z; say "z: ", 0+\$z;
for $x ($y, $z) {
say "i: ", 0+\$x;
my $f = sub { say "f: ", 0+\$x; };
$f->();
}
'
x: 159675200
y: 159675152
z: 159675312
i: 159675152
f: 159675152
i: 159675312
f: 159675312
修正:
for my $key (keys %hash) {
if (@{ $hash{$key} } > 1) {
for my $path (@{ $hash{$key} }) {
open(my $fh, "+<", $path) # +< ???
or die("Can't open \"$path\": $!\n");
my $tweak_server = sub {
my ($twig, $root) = @_;
...
};
my $twig = new XML::Twig->new(
TwigRoots => { TAG => 1 },
TwigHandlers => { 'ROOT/TAG' => $tweak_server },
twig_print_outside_roots => $fh, # No need for \*$fh
);
$twig->parsefile($path);
}
}
}
您还可以使用匿名包装器将变量作为参数传递给命名子。
sub tweak_server {
my ($fh, $key, $twig, $root) = @_;
...
}
for my $key (keys %hash) {
if (@{ $hash{$key} } > 1) {
for my $path (@{ $hash{$key} }) {
open(my $fh, "+<", $path) # +< ???
or die("Can't open \"$path\": $!\n");
my $twig = new XML::Twig->new(
TwigRoots => { TAG => 1 },
TwigHandlers => {
'ROOT/TAG' => sub { tweak_server($fh, $key, @_) },
},
twig_print_outside_roots => $fh, # No need for \*$fh
);
$twig->parsefile($path);
}
}
}