如何使这个嵌套循环在 perl 中继续



这是我的代码:

#!perl -w
use strict;
my %hash = (
    1 => "a",
    2 => "b",
);
foreach my $num ( keys %hash ) {
    while (<DATA>) {
        s/$num/$hash{$num}/g;
        print;
    }
}
__DATA__
121212
11111
222 

我打算用哈希中存在的相应值替换所有数字。但它输出:

a2a2a2
aaaaa
222
Hit any key to close this window...

为什么 foreach 循环只运行一次?谁能为我解释?我应该如何更改代码?我希望它输出:

ababab
aaaaa
bbb

提前谢谢。

foreach循环似乎只运行一次的原因是,您一次读取<DATA>一行。 第二次遍历外部循环时,不再有数据可以从内部循环中的数据中读取。 相反,你为什么不先将所有<DATA>读到一个列表中:

@mylist = <DATA>

然后在您的内部循环中循环此列表。

您可以将哈希的键组合到一个搜索模式中,而不是循环访问哈希中的每个键的文件,或者对文件进行口水运算。

请注意,在脚本的当前版本以及@Benj的答案中,应用替换的顺序是不确定的,因为键返回的键的顺序可能会在perl之间有所不同,甚至在使用相同perl的不同运行中也会有所不同。

这意味着你应该选择一个订单,并坚持下去,除非你喜欢惊喜。以下脚本结合了这两种想法。我决定将较长的键放在较短的键之前,这在大多数情况下是有意义的。

#!perl -w
use strict;
my %hash = qw(1 a 11 zz 2 b);
my $pat = join '|', 
          map qr/Q$_E/, 
          sort { length $b <=> length $a }
          keys %hash
          ;
while (<DATA>) {
    s/($pat)/$hash{$1}/g;
    print;
}
__DATA__
121212
11111
222 

输出:

阿巴巴兹扎嘟嘟��

代码的问题在于 Benj 指出的,DATA文件句柄在 foreach 的第二次迭代中处于 eof 处。而且在第一次迭代期间打印值。

如果您只打算将一个字符替换为另一个字符,即您的键/值的长度永远不会超过 1,我认为您应该改用 tr///

use strict;
use warnings;
while (<DATA>) {
    tr/12/ab/;
    print;
}
__DATA__
121212
11111
222 

音译运算符将一次性替换字符串中的所有字符。一个问题是tr///运算符必须是硬编码的,因此不能动态切换字符。

如果你想使用哈希,如果你的键/值可以大于1,你应该把Sinan的谨慎放在心上。例如,如果您同时拥有键111键,哪个应该优先于另一个?但是,如果是单个字符替换,这是一种顺利完成的方法:

my %hash = qw(1 a 2 b);
while (<DATA>) {
    s|(.)| $hash{$1} // $1 |ge;
    print;
}

请注意,使用正则表达式修饰符/e,它使替换评估 RHS 并插入返回值。在这种情况下,我们使用//运算符。它的作用是检查是否定义了哈希值,如果没有,只需使用键即可。似

if (defined $hash{$1}) {
    $hash{$1};
} else {
    $1;
}

defined $hash{$1} ? $hash{$1} : $1

另请注意,由于它一次使用(.)一个字符,因此它仅适用于单个字符键。

最简单的答案就是在while内做foreach,如

#!perl -w
use strict;
my %hash = (
    1 => "a",
    2 => "b",
);
while (<DATA>) {
    foreach my $num ( keys %hash ) {
        s/$num/$hash{$num}/g;
    }
    print;
}
__DATA__
121212
11111
222 

正如其他人所指出的,原始程序的问题在于DATA文件句柄的读取位置。rewind函数通常用于将文件句柄的位置重置回开头,以便可以再次读取,但在DATA的特定情况下,它无法正常工作,因为句柄实际上只是 perl 在.pl文件本身上的内部文件句柄。但是,您可以使用tell保存位置,然后使用以下seek每次迭代重置回该位置:

#!perl -w
use strict;
use Fcntl qw( SEEK_SET );
my %hash = (
    1 => "a",
    2 => "b",
);
my $pos = tell DATA;
foreach my $num ( keys %hash ) {
    seek DATA, $pos, SEEK_SET;
    while (<DATA>) {
        s/$num/$hash{$num}/g;
        print;
    }
}
__DATA__
121212
11111
222 

给出输出:

a2a2a2
aaaaa
222 
1b1b1b
11111
bbb 

最新更新