替换为Perl中的命名捕获和预编译正则表达式



我正试图编译一组替换正则表达式,但我不知道如何在我设置的替换标量中延迟捕获变量的插值;这里有一个简单的人为例子:

use strict;
use warnings;
my $from = "quick";
my $to = "zippy";
my $find = qr/${from} (?<a>(fox|dog))/;
my $repl = "$to $+{a}"; # Use of uninitialized value in concatenation (.) or string
my $s0 = "The quick fox...n";
$s0 =~ s/${find}/${repl}/;
print($s0);

这不起作用,因为repl被立即插值;在串联(.(或字符串"中使用未初始化的值;

如果我使用非插值的"引号,它不会在实际替换中插值,所以我得到";活泼的$+{a}">

有没有一个技巧可以把包含捕获引用的替换标量放在一边?

您收到警告是因为在执行匹配之前使用了$+{a}。CCD_ 2不进行任何匹配;它只是简单地编译模式。是s///执行匹配。

你大概是想用

my $repl = "$to $+{a}";

但这只是输出

The zippy $+{a}...

您可以使用以下内容:

my $find = qr/quick (?<a>fox|dog)/;
my $s0 = "The quick fox...n";
$s0 =~ s/$find/zippy $+{a}/;
print($s0);

但这对替换表达式进行了硬编码。如果您希望此代码是动态的,那么您正在构建的是一个模板系统。

我不知道有任何模板系统具有您所需的特定语法。

如果您可以使用位置变量($1(而不是命名变量($+{a}(,则可以使用String::Substitution。

use String::Substitution qw( sub_modify );
my $find = qr/quick (?<a>fox|dog)/;    # Or simply qr/Q$fromE (fox|dog)/
my $repl = "zippy $1";
my $s0 = "The quick fox...n";
sub_modify($s0, $find, $repl);
print($s0);

qr//只编译一个模式。它不执行匹配,因此在%+中不设置任何内容。因此,出现了未初始化的警告。

然而,你可以在替换中做到这一点,这样你就不需要提前准备替换:

s/$find/$to $+{a}/;

然而,如果你不知道你想要的替换是什么,你可以在替换的替换端评估代码,然后它将成为替换。这里有一个简单的补充:

s/$find/ 2 + 2 /e;

你会得到这笔钱作为替代:

The 4 jumped over the lazy dog

但问题是:这就是代码,它可以做任何代码能做的事情。如何构建它非常重要,永远不应该使用未初始化的用户输入。

如果您不知道要放入其中的字符串,可以预先构造它,并将其存储在替换端使用的变量中。但是,您正在将Perl代码制作为eval,因此它需要是一个有效的Perl字符串。双引号是您稍后评估的评估的一部分:

my $replacement = '"$to $+{a}"';
s/$find/$replacement/;

像这样,您可以从$replacement:中获得文字字符串值

The "$to $+{a}" jumped over the lazy dog

添加/e意味着我们将替换侧评估为代码:

s/$find/$replacement/e;

但是,该代码是$replacement,并且最终给了我们相同的结果,因为它只是它的字符串值:

The "$to $+{a}" jumped over the lazy dog

现在是有趣的部分。我们可以再次eval!添加另一个/e,替换将第一次求值,然后取该结果并再次求值:

$s0 =~ s/${find}/$replacement/ee;

eval的第一轮获取$replacement的文本值,即"$to $+{a}"(包括双引号(。第二轮采用"$to $+{a}"并评估它,用当前词法范围中的值填充变量。%+已由替换填充。现在你有了你的结果:

The zippy fox jumped over the lazy dog

然而,这不是一个你应该轻易退出的把戏。也许有更好的方法来解决你的问题。当你把任何其他事情都屈从于你的意愿时,你就会做这种事。

您还必须非常小心地在您构造的字符串中执行您想要的操作。您正在创建新的Perl代码。如果你使用的是你没有提供的任何类型的外部数据,有人可能会欺骗你的程序运行你不想要的代码。

有三种在运行时进行动态正则表达式替换的好方法:

  1. 变量s的字符串插值///
  2. 代码执行的回调s///e
  3. 正则表达式中的嵌入代码结构

请参阅下面的示例。

通常,当需要逻辑来构造替换时,会使用通过函数或嵌入式正则表达式代码的回调形式。

否则,在替换一侧使用简单的字符串插值。

use strict;
use warnings;
my $s0 = "";
my ($from, $to) = ("quick", "zippy") ;
sub getRepl {
my ($grp1, $grp2) = @_;
if ( $grp1 eq $from ) {
return "<$to $grp2>" }
else {
return "< $2>"
}
}
my $find = qr/(Q${from}E) (fox|dog)/;
# ======================================
# Substitution via string interpolation
$s0 = "The quick dog...n";
$s0 =~ s/$find/[$to $2]/;
print $s0;
# ======================================
# Substitution via callback (eval)
$s0 = "The quick dog...n";
$s0 =~ s/$find/ getRepl($1,$2) /e;
print $s0;

# ==================================================
# Substitution via regex embedded code constructs
my $repl = "";
my $RxCodeEmbed = qr/(Q${from}E)(?{$repl = '(' . $to}) (fox|dog)(?{$repl .= ' ' . $^N . ')'})/;
$s0 = "The quick dog...n";
$s0 =~ s/$RxCodeEmbed/$repl/;
print $s0;

输出

The [zippy dog]...
The <zippy dog>...
The (zippy dog)...

最新更新