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 strictuse 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;
}