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

时间:2016-12-22 09:44:33

标签: regex perl

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

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

但是当我将模式和替换移动到变量时,$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

当我使用“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

我在这里想念什么?

修改

$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

1 个答案:

答案 0 :(得分:10)

使用$p="b(.)d";,您将获得一个包含文字字符b(.)d的字符串。通常,正则表达式模式不会保留在带引号的字符串中,并且可能在正则表达式中没有它们的预期含义。但是,请在最后看到注意

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

对于替换部分和/ee,问题是首先评估$r,以产生_$1$1_,然后将其作为代码进行评估。唉,这不是有效的Perl代码。 _是裸字,即使$1$1本身无效(例如,$1 . $1也是如此。

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

'$1_$2$3other'  -->  $1 . '_' . $2 . $3 . 'other'

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

split在分隔符模式中捕获,有助于解决这个问题。

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

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

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

$var =~ s/$p/repl($r)/gee;

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

然后$N以外的每个术语都包含在'...'中,所以当它们全部由.加入时,我们会得到一个有效的Perl表达式,如上例所示。

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

我们被告知在外部输入上使用/ee的安全性不是问题。不过,这是值得记住的。请参阅this post在评论中提供的Håkon Hægland。除了讨论,它还指导我们String::Substitution。它的用法在this post中得到证明。另一种方法是使用Data::Munge

中的replace

有关/ee的更多讨论,请参阅this post,并提供一些有用的答案。

关于将"b(.)d"用于正则表达式

的注意事项

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