Perl 替换文件中的多个字符串
Perl replace multiple strings in a file
我无法用其他内容替换文件中的一组字符串。我已经检查了 Whosebug 和其他网站,但缺少一些东西。
我有一个包含以下内容的文件:
12 [ fillcolor="#62cb62", fontcolor="#ffffff", label="HealthStatus 12", shape=hexagon ]
520 [ fillcolor="#ffc0cb", fontcolor="#000000", label="A-SYNT 520", shape=parallelogram ]
521 [ fillcolor="#ffc0cb", fontcolor="#000000", label="A-CONF 521", shape=parallelogram ]
522 [ fillcolor="#ffc0cb", fontcolor="#000000", label="A-SAFE 522", shape=parallelogram ]
12 -> 522 [ color="#000000" ]
12 -> 521 [ color="#000000" ]
12 -> 520 [ color="#000000" ]
我要更换
12 [ with 12 HealthStatus [
520 [ with 520 A-SYNT [
521 [ with 521 A-CONF [
522 [ with 522 A-SAFE [
12 -> with 12 HealthStatus ->
我已经在数组中包含数字和字符串,并将它们保存在变量 $proxyid 和 $proxyname 中。
这是我正在使用的代码
rename($GVFILE, $GVFILE.'.bak');
open(OUT, '>'.$GVFILE) or die $!;
print_log ($IL, "## proxystack $#proxy_stack");
open(IN, '<'.$GVFILE.'.bak') or die $!;
for (my $cursor = 0; $cursor <= $#proxy_stack; $cursor++) {
$proxy_length = 0;
$proxyid = $proxy_stack[$cursor];
$proxyname = $proxy_stack[$cursor]{'name'};
print_log ($IL, "## debug $proxyid $proxyname");
print_log ($IL, "## debug cursor $cursor");
while(<IN>)
{
$_ =~ s/$proxyid \[/$proxyid $proxyname \[/g;
$_ =~ s/$proxyid -\>/$proxyid $proxyname -\>/g;
print OUT $_;
}
}
close(IN);
close(OUT);
我确定我在 $proxy_stack 中拥有所有内容,因为我正在打印它并且我得到了我期望的所有元素,但只执行了第一个替换。
您基本上是以错误的顺序进行循环:您在 @proxy_stack
上循环,然后在 <IN>
上循环,但您应该做相反的事情。因此,发生的事情是在 for (my $cursor = 0; $cursor <= $#proxy_stack; $cursor++)
的第一次迭代中,while 循环 while(<IN>)
读取整个文件,并对 @proxy_stack
的第一个元素执行替换。然后,它继续进行 for 循环的第二次迭代,但是您已经到达 IN
的末尾,因此 while(<IN>)
循环根本没有循环。
这会起作用:
use strict;
use warnings;
my $GVFILE = "t.txt";
rename $GVFILE, "$GVFILE.bak";
open my $out, '>', $GVFILE or die $!;
open my $in, '<', "$GVFILE.bak" or die $!;
my %proxies = ('12' => 'HealthStatus',
'520' => 'A-SYNT',
'521' => 'A-CONF',
'522' => 'A-SAFE');
while (<$in>) {
for my $proxy (keys %proxies) {
s/(\Q$proxy\E) (\[|->)/ $proxies{} /g;
}
print $out $_;
}
我做了一些修改:
我添加了 use strict
和 use warnings
,你应该经常这样做。 (我已经用 my
声明了变量,你也应该始终这样做,但你可能不必考虑太多,因为 strict
会在你忘记时提醒你)
我使用了 3 个参数 open
(你应该一直这样做)。
我使用词法文件句柄而不是全局文件句柄(你永远不应该使用全局文件句柄)。
我使用了哈希图而不是您的 @proxy_stack
数组。这看起来更简单,但您可能有正当理由使用数组,在这种情况下,我相信您将能够修改我的代码以改为使用数组。
我使用了 foreach
循环来遍历 %proxies
(对应于代码中的 @proxy_stack
)。它更地道,也更简洁。
根据您的实际代码,这可能看起来更简单和更有效(因为正则表达式引擎应该优化正则表达式中的 |
):
my %proxies = ('12 [' => '12 HealthStatus [',
'520 [' => '520 A-SYNT [',
'521 [' => '521 A-CONF [',
'522 [' => '522 A-SAFE [',
'12 ->' => '12 HealthStatus -> ');
my $proxies_match = join "|", map { quotemeta } keys %proxies;
while (<$in>) {
s/$proxies_match/$proxies{$&}/g;
print $out $_;
}
如果%proxies
只有这4个键,速度的影响可以忽略不计。如果 %proxies
有更多的键(并且每个键都是一个数字,后跟 ->
或 [
),那么这种方法有点乏味。
最后,Perl 有一个很好的就地文件编辑功能,它可以让你的代码更容易编写,但这将取决于你的脚本有多大(对于小脚本,它可能没问题;对于大的,可能不是):
$^I = '.bak'; # the extension for the backup file
# This also will make "print" print into the input file
@ARGV = 't.txt'; # the file to be read when doing `<>`
my %proxies = ('12' => 'HealthStatus',
'520' => ' A-SYNT',
'521' => 'A-CONF',
'522' => 'A-SAFE');
while (<>) {
for my $proxy (keys %proxies) {
s/(\Q$proxy\E) (\[|->)/ $proxies{} /g;
}
print;
}
我无法用其他内容替换文件中的一组字符串。我已经检查了 Whosebug 和其他网站,但缺少一些东西。 我有一个包含以下内容的文件:
12 [ fillcolor="#62cb62", fontcolor="#ffffff", label="HealthStatus 12", shape=hexagon ]
520 [ fillcolor="#ffc0cb", fontcolor="#000000", label="A-SYNT 520", shape=parallelogram ]
521 [ fillcolor="#ffc0cb", fontcolor="#000000", label="A-CONF 521", shape=parallelogram ]
522 [ fillcolor="#ffc0cb", fontcolor="#000000", label="A-SAFE 522", shape=parallelogram ]
12 -> 522 [ color="#000000" ]
12 -> 521 [ color="#000000" ]
12 -> 520 [ color="#000000" ]
我要更换
12 [ with 12 HealthStatus [
520 [ with 520 A-SYNT [
521 [ with 521 A-CONF [
522 [ with 522 A-SAFE [
12 -> with 12 HealthStatus ->
我已经在数组中包含数字和字符串,并将它们保存在变量 $proxyid 和 $proxyname 中。
这是我正在使用的代码
rename($GVFILE, $GVFILE.'.bak');
open(OUT, '>'.$GVFILE) or die $!;
print_log ($IL, "## proxystack $#proxy_stack");
open(IN, '<'.$GVFILE.'.bak') or die $!;
for (my $cursor = 0; $cursor <= $#proxy_stack; $cursor++) {
$proxy_length = 0;
$proxyid = $proxy_stack[$cursor];
$proxyname = $proxy_stack[$cursor]{'name'};
print_log ($IL, "## debug $proxyid $proxyname");
print_log ($IL, "## debug cursor $cursor");
while(<IN>)
{
$_ =~ s/$proxyid \[/$proxyid $proxyname \[/g;
$_ =~ s/$proxyid -\>/$proxyid $proxyname -\>/g;
print OUT $_;
}
}
close(IN);
close(OUT);
我确定我在 $proxy_stack 中拥有所有内容,因为我正在打印它并且我得到了我期望的所有元素,但只执行了第一个替换。
您基本上是以错误的顺序进行循环:您在 @proxy_stack
上循环,然后在 <IN>
上循环,但您应该做相反的事情。因此,发生的事情是在 for (my $cursor = 0; $cursor <= $#proxy_stack; $cursor++)
的第一次迭代中,while 循环 while(<IN>)
读取整个文件,并对 @proxy_stack
的第一个元素执行替换。然后,它继续进行 for 循环的第二次迭代,但是您已经到达 IN
的末尾,因此 while(<IN>)
循环根本没有循环。
这会起作用:
use strict;
use warnings;
my $GVFILE = "t.txt";
rename $GVFILE, "$GVFILE.bak";
open my $out, '>', $GVFILE or die $!;
open my $in, '<', "$GVFILE.bak" or die $!;
my %proxies = ('12' => 'HealthStatus',
'520' => 'A-SYNT',
'521' => 'A-CONF',
'522' => 'A-SAFE');
while (<$in>) {
for my $proxy (keys %proxies) {
s/(\Q$proxy\E) (\[|->)/ $proxies{} /g;
}
print $out $_;
}
我做了一些修改:
我添加了
use strict
和use warnings
,你应该经常这样做。 (我已经用my
声明了变量,你也应该始终这样做,但你可能不必考虑太多,因为strict
会在你忘记时提醒你)我使用了 3 个参数
open
(你应该一直这样做)。我使用词法文件句柄而不是全局文件句柄(你永远不应该使用全局文件句柄)。
我使用了哈希图而不是您的
@proxy_stack
数组。这看起来更简单,但您可能有正当理由使用数组,在这种情况下,我相信您将能够修改我的代码以改为使用数组。我使用了
foreach
循环来遍历%proxies
(对应于代码中的@proxy_stack
)。它更地道,也更简洁。
根据您的实际代码,这可能看起来更简单和更有效(因为正则表达式引擎应该优化正则表达式中的 |
):
my %proxies = ('12 [' => '12 HealthStatus [',
'520 [' => '520 A-SYNT [',
'521 [' => '521 A-CONF [',
'522 [' => '522 A-SAFE [',
'12 ->' => '12 HealthStatus -> ');
my $proxies_match = join "|", map { quotemeta } keys %proxies;
while (<$in>) {
s/$proxies_match/$proxies{$&}/g;
print $out $_;
}
如果%proxies
只有这4个键,速度的影响可以忽略不计。如果 %proxies
有更多的键(并且每个键都是一个数字,后跟 ->
或 [
),那么这种方法有点乏味。
最后,Perl 有一个很好的就地文件编辑功能,它可以让你的代码更容易编写,但这将取决于你的脚本有多大(对于小脚本,它可能没问题;对于大的,可能不是):
$^I = '.bak'; # the extension for the backup file
# This also will make "print" print into the input file
@ARGV = 't.txt'; # the file to be read when doing `<>`
my %proxies = ('12' => 'HealthStatus',
'520' => ' A-SYNT',
'521' => 'A-CONF',
'522' => 'A-SAFE');
while (<>) {
for my $proxy (keys %proxies) {
s/(\Q$proxy\E) (\[|->)/ $proxies{} /g;
}
print;
}