【问题标题】:perl: capturing the replaced-with stringperl:捕获替换为字符串
【发布时间】:2018-06-26 20:00:40
【问题描述】:

我的循环代码类似于

for( my $i=0; $a =~ s/<tag>(.*?)<\/tag>/sprintf("&CITE%03d;",$i)/e ; $i++ ){  
    %cite{ $i } = $1;  
    }

但不只是整数索引,我想让散列的键实际替换为文本(占位符“&CITE001;”等),而不必重做 sprintf()。

我几乎可以肯定有办法做到这一点(类似于 $& 等的变量,但也许我在考虑 vim 的替换而不是 perl。:)

谢谢!

【问题讨论】:

  • 按照@ikegami 的方式去做。如果您以这种方式进行循环,它将是一个无限循环,总是找到第一个 &lt;tag&gt;(.*?)&lt;\/tag&gt; 因为s///e 不是全局的,所以您需要s///eg 才能越过该匹配项。
  • 这看起来有点像xml。是吗?如果是这样,使用解析器很容易解决。
  • @sln,你错了。因为替换永远不会包含&lt;tag&gt;...&lt;/tag&gt;,所以它不是无限循环。然而,这是低效的,因为比赛每次都是从头开始。
  • @ikegami - 是的,我的疏忽。

标签: regex perl


【解决方案1】:
my $i = 0;
s{<tag>(.*?)</tag>}{
   my $entity = sprintf("&CITE%03d;", $i++);
   $cite{$entity} = $1;
   $entity
}eg;

【讨论】:

    【解决方案2】:

    我做了一些简单的事情,但我真的想要一些更优雅的东西。我最终做的(现在)是

    my $t;  
    for( my $i=0; $t = sprintf("&CITE%04d;",$i), $all =~ s/($oct.*?$cct)/$t/s; $i++ ){  
        $cites{$t} = $1;  
        }
    

    但我真的想要一些更“独立”的东西。

    不过,只要能够获取替换字符串,事情就会变得简单得多。这是一个简单的读-修改-写操作。

    没错,添加“g”修饰符应该有助于减少一些微秒。 :D

    【讨论】:

      【解决方案3】:

      我认为除了从目标开始重新开始搜索之外的任何方法
      总是是更好的选择。

      在这种情况下,作为替代方案,您可以在正则表达式中移动逻辑
      通过 代码构造 (?{ code }) 并利用 $^N 包含的事实
      最后捕获的内容。

      Perl

      use strict;
      use warnings;
      
      use Data::Dumper;
      $Data::Dumper::Sortkeys = 1;
      
      my $target = "<tag>zero</tag>\n<tag>one</tag>\n<tag>two</tag>\n<tag>three</tag>";
      
      my %cite;
      my ($cnt,$key) = (0,'');
      
      $target =~ s/
          <tag> (.*?) <\/tag>
          (?{
            $key = sprintf("&CITE%03d;", $cnt++);
            $cite{$key} = $^N;
          })
        /$key/xg;
      
      print $target, "\n";
      
      print Dumper(\%cite);
      

      输出

      &CITE000;
      &CITE001;
      &CITE002;
      &CITE003;
      $VAR1 = {
                '&CITE000;' => 'zero',
                '&CITE001;' => 'one',
                '&CITE002;' => 'two',
                '&CITE003;' => 'three'
              };
      

      @Ikegami 编辑/编码

      use strict;
      use warnings;
      
      use Data::Dumper;
      $Data::Dumper::Sortkeys = 1;
      
      sub f {
          my $target = "<tag>zero</tag>\n<tag>one</tag>\n<tag>two</tag>\n<tag>three</tag>";
      
          my %cite;
          my ($cnt,$key) = (0,'');
      
          $target =~ s/
              <tag> (.*?) <\/tag>
              (?{
                $key = sprintf("&CITE%03d;", $cnt++);
                $cite{$key} = $^N;
              })
            /$key/xg;
      
          print $target, "\n";
      
          print Dumper(\%cite);
      }
      
      f() for 1..2;
      

      输出

      Variable "$key" will not stay shared at (re_eval 1) line 2.
      Variable "$cnt" will not stay shared at (re_eval 1) line 2.
      Variable "%cite" will not stay shared at (re_eval 1) line 3.
      &CITE000;
      &CITE001;
      &CITE002;
      &CITE003;
      $VAR1 = {
                '&CITE000;' => 'zero',
                '&CITE001;' => 'one',
                '&CITE002;' => 'two',
                '&CITE003;' => 'three'
              };
      
      
      
      
      $VAR1 = {};
      

      此问题已在 5.18 中解决。


      @sln 编写的 Perl

      看,现在我在 5.20 版中没有遇到这个问题。
      而且,我也不相信我在 5.12 中得到了它。

      use strict;
      use warnings;
      
      use Data::Dumper;
      $Data::Dumper::Sortkeys = 1;
      
      sub wrapper {
         my ($targ, $href) = @_;
         my ($cnt, $key) = (0,'');
         $$targ =~ s/<tag>(.*?)<\/tag>(?{ $key = sprintf("&CITE%03d;", $cnt++); $href->{$key} = $^N; })/$key/g;
      }
      
      my ($target,%cite) = ("<tag>zero</tag>\n<tag>one</tag>\n<tag>two</tag>\n<tag>three</tag>", ());
      wrapper( \$target, \%cite );
      print $target, "\n";
      print Dumper(\%cite);
      
      ($target,%cite) = ("<tag>zero</tag>\n<tag>one</tag>\n<tag>two</tag>\n<tag>three</tag>", ());
      wrapper( \$target, \%cite );
      print $target, "\n";
      print Dumper(\%cite);
      

      输出

      &CITE000;
      &CITE001;
      &CITE002;
      &CITE003;
      $VAR1 = {
                '&CITE000;' => 'zero',
                '&CITE001;' => 'one',
                '&CITE002;' => 'two',
                '&CITE003;' => 'three'
              };
      &CITE000;
      &CITE001;
      &CITE002;
      &CITE003;
      $VAR1 = {
                '&CITE000;' => 'zero',
                '&CITE001;' => 'one',
                '&CITE002;' => 'two',
                '&CITE003;' => 'three'
              };
      

      【讨论】:

      • (?{ }) 中使用的三个变量的my 应该是local our。 (将代码移动到您调用两次的 sub 中,以查看 my 是错误的选择。)
      • 我认为(?{..}) 可以从外部范围引用变量就好了。在正则表达式中将它们设置为本地,只是使它们在展开量化范围时重置。就像柜台一样。本地正则表达式声明仅在这种情况下使用,其中外部作用域 var 在正则表达式末尾被分配本地 val。
      • 我告诉过你如何让失败变得明显。你明明没试过,为什么要反驳我? /// 你甚至不知道你的方法的陷阱这一事实只是表明它是多么复杂。我建议不要在很容易避免的情况下使用实验性(?{ })
      • 不确定你的意思,所以你必须生成一些我可以检查的代码。
      • 字面意思就是把你的代码放在一个sub中然后调用sub两次。
      猜你喜欢
      • 1970-01-01
      • 2014-10-06
      • 2019-01-15
      • 2022-01-08
      • 2013-03-08
      • 2012-09-21
      • 2015-10-11
      • 2014-12-03
      • 2021-12-05
      相关资源
      最近更新 更多