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

时间:2012-05-05 08:38:24

标签: perl hash

这是我的代码:

#!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

提前致谢。

4 个答案:

答案 0 :(得分:5)

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

@mylist = <DATA>

然后在内循环中遍历此列表。

答案 1 :(得分:1)

您可以将哈希的键组合成一种搜索模式,而不是为哈希中的每个键循环遍历文件,或者将文件粘贴到一个搜索模式中。

请注意,在当前版本的脚本以及@Benj's answer中,应用替换的顺序是 indeterminate ,因为{{3}返回的键的顺序使用相同的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 

输出:

ababab
zzzza
bbb

答案 2 :(得分:1)

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

另请注意,由于当时(.)占用一个字符,因此仅适用于单个字符键。

答案 3 :(得分:0)

最简单的答案就是在foreach内执行while,因为

#!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的特定情况下,它无法正常工作,因为这种方式句柄实际上只是.pl文件本身的perl内部文件句柄。但是,您可以使用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