Perl:如何在正则表达式中使用字符串变量作为搜索模式和替换

kan*_*yin 8 regex perl

我想在正则表达式中使用字符串变量进行搜索模式和替换.预期的输出是这样的,

$ perl -e '$a="abcdeabCde"; $a=~s/b(.)d/_$1$1_/g; print "$a\n"'
a_cc_ea_CC_e
Run Code Online (Sandbox Code Playgroud)

但是当我将模式和替换移动到变量时,$1没有进行评估.

$ perl -e '$a="abcdeabCde"; $p="b(.)d"; $r="_\$1\$1_"; $a=~s/$p/$r/g; print "$a\n"'
a_$1$1_ea_$1$1_e
Run Code Online (Sandbox Code Playgroud)

当我使用"ee"修饰符时,它会出错.

$ perl -e '$a="abcdeabCde"; $p="b(.)d"; $r="_\$1\$1_"; $a=~s/$p/$r/gee; print "$a\n"'
Scalar found where operator expected at (eval 1) line 1, near "$1$1"
    (Missing operator before $1?)
Bareword found where operator expected at (eval 1) line 1, near "$1_"
    (Missing operator before _?)
Scalar found where operator expected at (eval 2) line 1, near "$1$1"
    (Missing operator before $1?)
Bareword found where operator expected at (eval 2) line 1, near "$1_"
    (Missing operator before _?)
aeae
Run Code Online (Sandbox Code Playgroud)

我在这里想念什么?


编辑

这两个$p$r被我自己写的.我需要的是做多类似的正则表达式替换而不触及Perl代码,所以$p$r必须在一个单独的数据文件.我希望以后可以将此文件与C++/python代码一起使用.以下是一些$p和的例子$r.

^(.*\D)?((19|18|20)\d\d)?   $1$2<digits>?
^(.*\D)?(0\d)?  $1$2<digits>?
([TKZGD])(\d+)/(\d+)([^\d/])    $1$2<digits>$3<digits>$4
([^/TKZGD\d])(\d+)/(\d+)([^/\d])    $1$3??$2$4
Run Code Online (Sandbox Code Playgroud)

zdi*_*dim 10

随着$p="b(.)d";你越来越有文字字符的字符串b(.)d.通常,正则表达式模式不会保留在带引号的字符串中,并且可能在正则表达式中没有它们的预期含义.但是,请参见最后的注释.

这就是qr运算符的用途:$p = qr/b(.)d/;将字符串形式化为正则表达式.

至于替换部分/ee,问题是$r首先评估,产生_$1$1_,然后评估为代码.唉,这不是有效的Perl代码.的_是裸字,甚至$1$1本身是无效的(例如,$1 . $1是).

所提供的例子$r有无$Ns的以各种方式文本混合.解析它的一种方法是将所有$N和所有其他内容提取到一个列表中,该列表从字符串维护它们的顺序.然后,可以将其处理为将成为有效代码的字符串.例如,我们需要

'$1_$2$3other'  -->  $1 . '_' . $2 . $3 . 'other'
Run Code Online (Sandbox Code Playgroud)

这是可以评估的有效Perl代码.

分裂模式中分裂的捕获有助于打破这一点.

sub repl {
    my ($r) = @_;

    my @terms = grep { $_ } split /(\$\d)/, $r;

    return join '.', map { /^\$/ ? $_ : q(') . $_ . q(') } @terms;
}

$var =~ s/$p/repl($r)/gee;
Run Code Online (Sandbox Code Playgroud)

捕获/(...)/in split的模式后,分隔符将作为列表的一部分返回.因此,这从$r一系列术语中提取,这些术语是$N其原始顺序中的任一个或其他,并且保留了所有内容(除了尾随空格).这包括可能的(前导)空字符串,因此需要将其过滤掉.

然后包含除$Ns 之外的每个术语'...',因此当它们全部加入时,.我们得到一个有效的Perl表达式,如上例所示.

然后/ee将使此函数返回字符串(如上所述),并将其评估为有效代码.

我们被告知/ee在这里不需要考虑使用外部输入的安全性.不过,这是值得记住的.请参阅HåkonHægland在评论中提供的这篇文章.除了讨论,它还指导我们String :: Substitution.它的用途在这篇文章中得到了证明.解决这个另一种方式是从数据:: Munge时间replace

对于更多的讨论/ee这个帖子,有几个有用的答案.


关于使用"b(.)d"正则表达式模式的注意事项

在这种情况下,使用parens和dot,可以保持其特殊含义.感谢kangshiyin早期提到这个,并感谢HåkonHægland断言它.但是,这是一个特例.双引号字符串直接拒绝许多模式,因为插值已完成 - 例如,"\w"只是一个转义w(无法识别).该引号应该工作,因为没有插值.尽管如此,用作正则表达式模式的字符串最好使用qr,因为我们正在获得真正的正则表达式.然后也可以使用所有修饰符.