这是我的代码:
#!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
提前致谢。
答案 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的注意事项放在心上。例如,如果你有两个键1
和11
,应该优先于另一个?但是,如果是单字符替换,这是一种顺利完成的方法:
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