我想在正则表达式中使用字符串变量进行搜索模式和替换.预期的输出是这样的,
$ 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
有无$N
s的以各种方式文本混合.解析它的一种方法是将所有$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
其原始顺序中的任一个或其他,并且保留了所有内容(除了尾随空格).这包括可能的(前导)空字符串,因此需要将其过滤掉.
然后包含除$N
s 之外的每个术语'...'
,因此当它们全部加入时,.
我们得到一个有效的Perl表达式,如上例所示.
然后/ee
将使此函数返回字符串(如上所述),并将其评估为有效代码.
我们被告知/ee
在这里不需要考虑使用外部输入的安全性.不过,这是值得记住的.请参阅HåkonHægland在评论中提供的这篇文章.除了讨论,它还指导我们String :: Substitution.它的用途在这篇文章中得到了证明.解决这个另一种方式是从数据:: Munge时间replace
对于更多的讨论/ee
看这个帖子,有几个有用的答案.
关于使用"b(.)d"
正则表达式模式的注意事项
在这种情况下,使用parens和dot,可以保持其特殊含义.感谢kangshiyin早期提到这个,并感谢HåkonHægland断言它.但是,这是一个特例.双引号字符串直接拒绝许多模式,因为插值已完成 - 例如,"\w"
只是一个转义w
(无法识别).该单引号应该工作,因为没有插值.尽管如此,用作正则表达式模式的字符串最好使用qr
,因为我们正在获得真正的正则表达式.然后也可以使用所有修饰符.