regexp替换取决于匹配值

时间:2014-04-27 11:12:57

标签: regex perl

WITH SINGLE REGEXP是否可以根据匹配的值执行不同的替换?

例如,给定字符串“aaa bbb ccc aaa ddd”我想用“alpha”取代每次出现的“aaa”,用“beta”取代“bbb”,用“gamma”和“ddd取代”ccc“ “与”delta“。

我知道,四个单独的正则表达式替换非常容易:

my $s = "aaa bbb ccc aaa ddd";
$s =~ s/aaa/alpha/g;
$s =~ s/bbb/betaa/g;
$s =~ s/ccc/gamma/g;
$s =~ s/ddd/delta/g;

问题是,是否可以只用一个语句来做同样的事情:

$s =~ s/$pattern/$replacement/g;

如果重要,我正在使用perl。

这只是我想要解决的更复杂问题的简化示例;请不要开始争论我说的是错误的问题而且我应该采取不同的方式......如果你真的这么认为,请忽略这个问题。

2 个答案:

答案 0 :(得分:4)

您可以使用哈希替换匹配所需的值,

my %replacement = (
    "aaa" => "alpha",
    "bbb" => "beta", 
    "ccc" => "gamma",
    "ddd" => "delta",
);
my ($pattern) = map qr/$_/, join "|", map quotemeta, keys %replacement;

$s =~ s/($pattern)/$replacement{$1}/ge;

答案 1 :(得分:1)

可以提供的一件事是/e modifier to s///,,这意味着右侧被解释为代码,匹配的文本被替换为此代码返回的值。

对于您的示例,您需要以下内容:

$s =~ s/aaa|bbb|ccc/ $& eq 'aaa' ? 'alpha' : $& eq 'bbb' ? 'beta' : 'gamma' /eg;

或者更好(因为使用$&会导致性能下降):

$s =~ s/(aaa|bbb|ccc)/ $1 eq 'aaa' ? 'alpha' : $1 eq 'bbb' ? 'beta' : 'gamma' /eg;

顺便说一下,如果你的表达式变得非常大,你可能还想使用不同的分隔符和/x修饰符来提高可读性。例如:

$s =~ s
        { ( aaa | bbb | ccc ) }
        {
            $1 eq 'aaa'
                ? 'alpha'
                : $1 eq 'bbb'
                    ? 'beta'
                    : 'gamma'
        }egx;

编辑:质量

我提出了这个答案,因为问题要求替换取决于匹配的值,但没有说明在他们的实际问题中(与他们显示的简化版本相反),对匹配值的测试是一个简单的平等测试。

有人提出基于/e的解决方案质量低于基于散列的解决方案。这种观察在某种程度上是有效的。在我看来,解决方案不同的两个质量因素是:

  • 易读性:哈希解决方案在这一点上是一个无可置疑的赢家。
  • 资源使用:在OP提出的示例中,/e解决方案在时间和空间使用方面都获胜¹。如果问题变得更大,这两个解决方案将以不同的方式扩展:/e解决方案将在时间上是线性的并且在空间中是恒定的,而散列解决方案将在时间上(大致)恒定并且在空间中是线性的。

  1. 在本地,/e的速度提高了5-10%,%replacement使用了588个字节的内存。基准代码是:

    my $ str ='aaa bbb ccc aaa ddd'; my $ pattern = qr {(aaa | bbb | ccc | ddd)} x; my%replacement =(“aaa”=>“alpha”,“bbb”=>“beta”,“ccc”=>“gamma”,“ddd”=>“delta”,); cmpthese(10000000,     {         e => sub {my $ s = $ str; $ s = ~s / $ pattern / $ 1 eq'aaa'? 'alpha':$ 1 eq'bbb'? 'beta':$ 1 eq'c'? 'gamma':'delta'/ eg},         h => sub {my $ s = $ str; $ s = ~s / $ pattern / $ replacement {$ 1} / ge; },     } );