3

这是我的代码:

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

4 回答 4

5

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

@mylist = <DATA>

然后在你的内部循环中循环这个列表。

于 2012-05-05T08:42:14.847 回答
1

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

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

use strict;
use warnings;

while (<DATA>) {
    tr/12/ab/;
    print;
}

__DATA__
121212
11111
222 

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

如果你想使用哈希,如果你的键/值可以长于 1,你应该把思南的谨慎放在心上。例如,如果你有两个键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

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

于 2012-05-05T15:36:50.243 回答
1

您可以将散列的键组合成一个搜索模式,而不是为散列中的每个键循环遍历文件,或者对文件进行 slurping 操作。

请注意,在脚本的当前版本以及@Benj 的答案中,应用替换的顺序是不确定的,因为键返回的的顺序可能在perls 之间甚至在使用相同的不同运行时有所不同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 

输出:

阿里巴巴
zzzza
bbb
于 2012-05-05T11:15:04.323 回答
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它不能正常工作,因为句柄实际上只是 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 
于 2012-05-06T15:51:11.000 回答