使用已弃用的$ *重写旧程序

Mor*_*rti 7 perl

我们有一些非常老的Perl代码最近一次更新是在1997年。我正在尝试升级到$*不推荐使用的新Perl版本。

我一直在尝试学习如何重写它,但是从perlvar文档中获得的唯一帮助是“您应该使用/ s和/ m regexp修饰符。”

  my ($file, $regexp, $flags) = @_;
  my (@found_lines, @tmp_list, $comp_buf);
  local ($*);

  if ($flags =~ tr/c//d)
  {
    $* = 1;
    (substr ($regexp, 0, 1) ne "^") && ($regexp = "^.*$regexp");
    ($regexp !~ /([^\\]|^)(\\\\)*\$$/) && ($regexp .= ".*\$");
    &read_comp ($file, \$comp_buf);
    @found_lines = grep ($_ .= "\n", ($comp_buf =~ /$regexp/g));
  }
  else
  {
    @tmp_list = &read_list ($file, 0);
    @found_lines = grep (/$regexp/, @tmp_list);
  }

  if ($flags eq "q")
  {
    $#found_lines >= 0;
  }
  elsif ($flags eq "a")
  {
    $#found_lines+1;
  }
  else
  {
    @found_lines;
  }
Run Code Online (Sandbox Code Playgroud)

$*从我$*在这里使用的注释中了解的内容来看,要为以下正则表达式搜索启用多行匹配,真的很难知道如何为我替换。所以我猜我必须以某种方式将这些标志添加到regexp表达式中。

如何重写此代码以替换现有$*实例?

mel*_*ene 10

不幸的$*是,它是一个全局变量,因此,read_comp如果使用正则表达式,对其进行设置会影响所有调用的函数(例如)。

同样,该代码以一种奇怪的方式编写:

  • 我以为是要为该$comp_buf =~ /$regexp/g零件启用“多行”匹配,但$*设置较早,因此也会影响$regexp !~ /([^\\]|^)(\\\\)*\$$/read_comp调用。

  • 检查是否$regexp已经分别以^/ 开头/结尾$被破坏。例如,(?:^foo$)是锚定的正则表达式,但是代码无法检测到。

  • grep ($_ .= "\n", ...)grep对模仿的莫名其妙的滥用map。代码试图做的是获取正则表达式匹配的行列表。但是,正则表达式的构建方式与"\n"每一行上的终止换行符都不匹配,因此该代码将手动添加"\n"到每个返回的字符串中。

    这样做的理智方式是:

    @found_lines = map $_ . "\n", ...;   # or map "$_\n", ...
    
    Run Code Online (Sandbox Code Playgroud)

    取而代之的是,map我们利用命令循环for别名为当前列表元素的事实,使用命令式循环:

    @temp = ...;
    for (@temp) {
        $_ .= "\n";
    }
    @found_lines = @temp;
    
    Run Code Online (Sandbox Code Playgroud)

    除了for循环之外,我们还可以使用grep其遍历列表的副作用:

    @temp = ...;
    grep $_ .= "\n", @temp;
    @found_lines = @temp;
    
    Run Code Online (Sandbox Code Playgroud)

    grep也是$_当前元素的别名,因此“过滤器表达式”可以修改我们要遍历的列表。

    最后,因为.=返回结果字符串(且包含的字符串"\n"不能为false),所以我们可以利用以下事实:我们的“过滤器表达式”始终返回一个真值,并有效地从以下位置获取输入列表的副本作为返回值grep

    @found_lines = grep $_ .= "\n", ...  # blergh
    
    Run Code Online (Sandbox Code Playgroud)

至于的效果$*:它是一个布尔标志(最初为false)。如果设置为true,所有的正则表达式表现得好像/m是有效的,即^$比赛在嵌入式换行符以及字符串的开始/结束。

假设我对代码的解释是正确的,则应该可以进行如下更改:

  • local ($*); 可以删除。
  • $* = 1; 还需要走。
  • $comp_buf =~ /$regexp/g应该更改为$comp_buf =~ /$regexp/mg。这是我看到的唯一的多行模式有意义的地方。
  • 我真的很想重写最后一行。要么

    @found_lines = map "$_\n", ($comp_buf =~ /$regexp/g);
    
    Run Code Online (Sandbox Code Playgroud)

    (实用样式),或者,如果您更喜欢命令式样式:

    @found_lines = ($comp_buf =~ /$regexp/g);
    $_ .= "\n" for @found_lines;
    
    Run Code Online (Sandbox Code Playgroud)