这是我的代码:
#!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的谨慎放在心上。例如,如果您同时拥有键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
另请注意,由于它一次使用(.)
一个字符,因此它仅适用于单个字符键。
最简单的答案就是在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