【问题标题】:How to use a variable as modifier in a substitution如何在替换中使用变量作为修饰符
【发布时间】:2011-03-15 09:06:23
【问题描述】:

有没有办法在替换中使用变量作为修饰符?

my $search = 'looking';
my $replace = '"find: $1 ="';
my $modifier = 'ee';

s/$search/$replace/$modifier;

我需要使用哈希数组来使用不同的修饰符进行批量搜索替换。

【问题讨论】:

  • 一些修饰符可以在正则表达式中作为(?modifier:pattern) 提供,但不能像/g 或/e 那样影响整个事物。这是那些“你为什么要这样做”的时刻之一。如果您告诉我们这是做什么用的,我们也许可以想出一个更简单的方法,而不是刷骆驼毛(并打开一个安全漏洞)。
  • @Schwern:如果你在你的骆驼上开个洞,那是最不愉快的

标签: regex perl variables substitution modifier


【解决方案1】:

虽然使用eval 编译新替换的方法可能是最直接的,但您可以创建更模块化的替换:

use warnings;
use strict;

sub subst {
    my ($search, $replace, $mod) = @_;

    if (my $eval = $mod =~ s/e//g) {
        $replace = qq{'$replace'};
        $replace = "eval($replace)" for 1 .. $eval;
    } else {
        $replace = qq{"$replace"};
    }
    sub {s/(?$mod)$search/$replace/ee}
}

my $sub = subst '(abc)', 'uc $1', 'ise';

local $_ = "my Abc string";

$sub->();

print "$_\n";  # prints "my ABC string"

这只是轻微测试,留作练习供读者实现其他标志,如g

【讨论】:

  • 为什么每次替换都有一个 'ee' 修饰符?
  • @runrig => 似乎需要在不评估实际替换的情况下让嵌入式评估正常工作。可能有更好的方法,我没有花时间优化它。
【解决方案2】:

如果您戴上安全护目镜和除零套装,您可以使用eval

例如:

use strict;
use warnings;
sub mk_re {
  my ($search, $replace, $modifier) = @_;
  $modifier ||= '';
  die "Bad modifier $modifier" unless $modifier =~ /^[msixge]*$/;
  my $sub = eval "sub { s/($search)/$replace/$modifier; }";
  die "Error making regex for [$search][$replace][$modifier]: $@" unless $sub;
  return $sub;
}

my $search = 'looking';
my $replace = '"find: $1 ="';
my $modifier = 'e';

# Sub can be stored in an array or hash
my $sub = mk_re($search, $replace, $modifier);

$_ = "abc-looking-def";
print "$_\n";
$sub->();
print "$_\n";

【讨论】:

  • 我的除零套装在负无穷附近有个洞
【解决方案3】:

嗯,如果我必须这样做,我会这样做:

use warnings;
use strict;
my @stuff = (
{
    search => "this",
    replace => "that",
    modifier => "g",
},
{
    search => "ono",
    replace => "wendy",
    modifier => "i",
}
);
$_ = "this ono boo this\n";
for my $h (@stuff) {
    if ($h->{modifier} eq 'g') {
        s/$h->{search}/$h->{replace}/g;
    } elsif ($h->{modifier} eq 'i') {
        s/$h->{search}/$h->{replace}/i;
    }
    # etc.
}
print;

您可能想要使用的不同修饰符只有这么多,所以我认为这很容易。

您可以为此使用eval,但它非常混乱。

【讨论】:

  • 杂乱无章在旁观者的眼中。我发现这比 eval 解决方案更混乱。
  • @runrig: 在这种情况下,“混乱”并不是指代码的样子。如果您使用eval,则需要注意许多难以跟踪的错误。
  • 这是我在询问 Stackoverflox 之前考虑的解决方案。
  • 这个解决方案的麻烦在于重复的代码,很难维护,如果有 6 个修饰符,则意味着有 63 种可能的组合(不考虑顺序)。如果你在一个if中改变了一些东西,你需要在所有的地方都改变它,这对于代码维护来说是很糟糕的。我认为最好使用受控的 eval() 动态生成可能性,一种可能的方式是我发布的答案。
  • 使用 eval 您不必在每次使用时都编译正则表达式。如果在同一会话中多次执行这些替换,我可能会使用 eval。
【解决方案4】:

当然s/$search/$replace/ 可以按您的预期工作。动态修饰符并不简单。

对于pimsx 的常规匹配modifiers,您可以使用Perl 的Extended Patterns 动态修改修饰符标志作为模式的一部分。这些是(?pimsx-imsx) 的形式,用于打开/关闭这些修饰符。

对于 s// eee 表单,您可以使用同一 perlre 部分中记录的 (?{ perl code})。对于所有evaleee 表单,请考虑生成代码的安全性!

据我所知,没有将全局修改为第一个匹配的形式,因此全局与第一个匹配需要是单独的语句。

【讨论】:

    【解决方案5】:

    这是 Kinopiko 的回答和评估的组合。

    eval在这里用于以可控和可维护的方式生成查找表,并且查找表用于保存所有看起来不太有趣的if..elsif..elsif。

    (非常轻微的测试)

    my @stuff = (
    {
        search => "this",
        replace => "that",
        modifier => "g",
    },
    {
        search => "ono",
        replace => "wendy",
        modifier => "i",
    }
    );
    $_ = "this ono boo this\n";
    
    my @modifiers = qw{m s i x g e};
    
    my $s_lookup = {};
    
    foreach my $modifier (@modifiers) { 
        $s_lookup->{$modifier} =  eval " sub { s/\$_[0]/\$_[1]/$modifier } ";
    }
    
    for my $h (@stuff) {
        $s_lookup->{$h->{modifier}}->($h->{search},$h->{replace});
    }
    
    print; 
    

    要完全有用,这需要:

    1. 可能的修饰符组合
    2. 查找表上的排序功能,因此“msi”组合和“mis”组合将转到同一个键。

    【讨论】:

    • 或者只是将修饰符验证检查添加到其他评估答案,例如现在的答案。
    猜你喜欢
    • 2014-02-16
    • 2013-03-05
    • 1970-01-01
    相关资源
    最近更新 更多