Regex 在perl中,是否有一种更简洁的方法来搜索多个模式,并用表达式替换每个模式

Regex 在perl中,是否有一种更简洁的方法来搜索多个模式,并用表达式替换每个模式,regex,perl,replace,Regex,Perl,Replace,在perl中,我正在读取一行代码,并尝试使用一系列if语句将一组字符串替换为相应的表达式。例如: my @shiftInstructions=("lsr", "lsl", "rol", "ror"); while (my $line = <>) { if ($line =~ /\$sh/) { my $r = int(rand(6)); $line =~ s/\$sh/$r/; } if ($line =~ /\$ish/) { my $r = $

在perl中,我正在读取一行代码,并尝试使用一系列if语句将一组字符串替换为相应的表达式。例如:

my @shiftInstructions=("lsr", "lsl", "rol", "ror");
while (my $line = <>) {
  if ($line =~ /\$sh/) {
    my $r = int(rand(6));
    $line =~ s/\$sh/$r/;
  }
  if ($line =~ /\$ish/) {
    my $r = $shiftInstructions[rand(4)]
    $line =~ s/\$ish/$r/;
  }
}
my@shiftInstructions=(“lsr”、“lsl”、“rol”、“ror”);
while(我的$line=){
如果($line=~/\$sh/){
我的$r=int(兰特(6));
$line=~s/\$sh/$r/;
}
如果($line=~/\$ish/){
my$r=$shiftInstructions[rand(4)]
$line=~s/\$ish/$r/;
}
}
我不喜欢这种方法有很多原因。首先,它是重复的。我必须首先检查模式是否存在,如果存在,则执行函数生成替换值,然后替换。因此它既冗长又缓慢(每个模式搜索2个正则表达式,可能最终会搜索几十个模式字符串)

我想到了一个映射,其中许多代码映射到要执行的相应代码

我可以想象映射到一个字符串,然后使用eval,但是除了在运行时,我无法检查代码。有没有更干净的方法

我在regex中找到了execute选项。编写一组子例程来处理每个正则表达式,然后创建一个映射,怎么样

my %regexMap = (
    "\$fn", &foundFunc,
    "\$hw", &hex8,
    "\$hb", &hex2,
    "\$sh", &rand6,
    "\$ish", &shiftInst,
    );
while (my $line = <>) {
    $line =~ s/(\$fn|\$hw|\$hb|\$sh|\$ish|)/$regexMap{$1}/e;    
    print $line;
}
my%regexMap=(
“\$fn”、&foundFunc、,
“\$hw”、&hex8、,
“\$hb”、&hex2、,
“\$sh”&rand6,
“\$ish”、&shiftInst、,
);
while(我的$line=){
$line=~s/(\$fn\$hw\$hb\$sh\$ish)/$regexMap{$1}/e;
打印$行;
}

在这些情况下,我经常创建某种数据结构来保存模式及其操作:

my @tuples = (
    [ qr/.../, sub { ... } ]
    [ ... ].
    );
现在,无论我想尝试多少种模式,整个过程的内容都保持不变:

while( <> ) {
    foreach $tuple ( @tuples ) {
        $tuple->[1]() if /$tuple[0]/
        }
     }
但同样,它取决于输入、您可能希望匹配的实际子字符串数量(因此,交替中的分支数量)、您希望处理的行数,等等。关于如何处理apache日志文件,以及他们试图使事情更快的各种实验,有一个精彩的讨论。我已经忘记了所有的细节,但是非常小的调整在数千万行中产生了明显的差异

有趣的阅读:

  • 也许这次谈话

在这些情况下,我通常会创建某种数据结构来保存模式及其操作:

my @tuples = (
    [ qr/.../, sub { ... } ]
    [ ... ].
    );
现在,无论我想尝试多少种模式,整个过程的内容都保持不变:

while( <> ) {
    foreach $tuple ( @tuples ) {
        $tuple->[1]() if /$tuple[0]/
        }
     }
但同样,它取决于输入、您可能希望匹配的实际子字符串数量(因此,交替中的分支数量)、您希望处理的行数,等等。关于如何处理apache日志文件,以及他们试图使事情更快的各种实验,有一个精彩的讨论。我已经忘记了所有的细节,但是非常小的调整在数千万行中产生了明显的差异

有趣的阅读:

  • 也许这次谈话

    • OP的代码可以用以下形式编写

      use strict;
      use warnings;
      use feature 'say';
      
      my %regexMap = (
          '$fn'   => \&foundFunc,
          '$hw'   => \&hex8,
          '$hb'   => \&hex2,
          '$sh'   => \&rand6,
          '$ish'  => \&shiftInst,
          );
      
      my @keys = map { "\\$_" } keys %regexMap;
      my $re = join('|', @keys);
      
      while (<DATA>) {
          chomp;
          next unless /($re)/;
          $regexMap{$1}->();
      }
      
      sub foundFunc   { say 'sub_foundFunc' }
      sub hex8        { say 'sub_hex8'      }
      sub hex2        { say 'sub_hex2'      }
      sub rand6       { say 'sub_rand6'     }
      sub shiftInst   { say 'sub_shiftInst' }
      
      __DATA__
      $fn
      $hw
      $ac
      $hb
      $sh
      $fn
      $mf
      $hb
      $ish
      $hw
      

      OP的代码可以用以下形式编写

      use strict;
      use warnings;
      use feature 'say';
      
      my %regexMap = (
          '$fn'   => \&foundFunc,
          '$hw'   => \&hex8,
          '$hb'   => \&hex2,
          '$sh'   => \&rand6,
          '$ish'  => \&shiftInst,
          );
      
      my @keys = map { "\\$_" } keys %regexMap;
      my $re = join('|', @keys);
      
      while (<DATA>) {
          chomp;
          next unless /($re)/;
          $regexMap{$1}->();
      }
      
      sub foundFunc   { say 'sub_foundFunc' }
      sub hex8        { say 'sub_hex8'      }
      sub hex2        { say 'sub_hex2'      }
      sub rand6       { say 'sub_rand6'     }
      sub shiftInst   { say 'sub_shiftInst' }
      
      __DATA__
      $fn
      $hw
      $ac
      $hb
      $sh
      $fn
      $mf
      $hb
      $ish
      $hw
      
      这是一种糟糕的写作方式

      $line =~ s/\$sh/ int(rand(6)) /e;
      
      所以

    • $regexMap{$1}
      现在返回引用。您希望调用引用的子对象,这可以使用
      $regexMap{$1}->()
      完成

      while(我的$line=){
      $line=~s/(\$fn\$hw\$hb\$sh\$ish)/$regexMap{$1}->()/e;
      打印$行;
      }
      
    • 这是一种糟糕的写作方式

      $line =~ s/\$sh/ int(rand(6)) /e;
      
      所以

    • $regexMap{$1}
      现在返回引用。您希望调用引用的子对象,这可以使用
      $regexMap{$1}->()
      完成

      while(我的$line=){
      $line=~s/(\$fn\$hw\$hb\$sh\$ish)/$regexMap{$1}->()/e;
      打印$行;
      }
      

    • 这更加优雅,但您仍在处理大量正则表达式。我刚刚在正则表达式上找到了execute选项。我将编辑以添加我发现的内容,但如果有反对的理由,请让我知道。您可以根据需要进行调整。您没有询问如何选择要检查的模式集,在某个时刻您必须决定要检查的模式集。这更优雅,但您仍然在处理大量正则表达式。我刚刚在正则表达式上找到了execute选项。我将编辑以添加我发现的内容,但如果有反对的理由,请让我知道。您可以根据需要进行调整。你没有问如何选择要检查的模式集,在某个时候你必须决定要检查的模式集。这是行不通的。不管输入是什么,它都会调用每个子对象一次,而且只调用一次。即使你解决了这个问题,它仍然不能满足OP的要求,这是行不通的。不管输入是什么,它都会调用每个子对象一次,而且只调用一次。即使你解决了这个问题,它仍然不能满足OP的要求,这与你的要求相反。
      my @shiftInstructions=("lsr", "lsl", "rol", "ror");
      while (my $line = <>) {
        if ($line =~ /\$sh/) {
          my $r = int(rand(6));
          $line =~ s/\$sh/$r/;
        }
        if ($line =~ /\$ish/) {
          my $r = $shiftInstructions[rand(4)]
          $line =~ s/\$ish/$r/;
        }
        print($line);
      }
      
      my @shiftInstructions = qw( lsr lsl rol ror );
      
      while (my $line = <>) {
         $line =~ s/\$sh/ int(rand(6)) /e;
         $line =~ s/\$ish/ $shiftInstructions[rand(@shiftInstructions)] /e;
         print($line);
      }
      
      my @shiftInstructions = qw( lsr lsl rol ror );
      
      while (my $line = <>) {
         $line =~ s/\$(sh|ish)/
            if    ( $1 eq "sh"  ) { int(rand(6)) }
            elsif ( $1 eq "ish" ) { $shiftInstructions[rand(@shiftInstructions)] }
         /eg;
         print($line);
      }
      
      my @shiftInstructions = qw( lsr lsl rol ror );
      
      my %replacements = (
         sh  => sub { int(rand(6)) },
         ish => sub { $shiftInstructions[rand(@shiftInstructions)] },
      );
      
      my $alt = join '|', map quotemeta, keys(%replacements);
      my $re = qr/\$($alt)/;
      
      while (my $line = <>) {
         print $line =~ s/$re/ $replacements{$1}->() /reg;
      }
      
      my %regexMap = (
          "\$fn", \&foundFunc,
          "\$hw", \&hex8,
          "\$hb", \&hex2,
          "\$sh", \&rand6,
          "\$ish", \&shiftInst,
          );
      
      while (my $line = <>) {
          $line =~ s/(\$fn|\$hw|\$hb|\$sh|\$ish|)/ $regexMap{$1}->() /e;
          print $line;
      }