使用外部参数的 Perl 正则表达式替换

Perl regex substitution using external parameters

考虑以下示例:

my $text = "some_strange_thing";
$text =~ s/some_(\w+)_thing/no__stuff/;
print "Result: $text\n";  

它打印

"Result: no_strange_stuff"

到目前为止一切顺利。

现在,我需要从外部来源(用户输入、配置文件等)获取匹配和替换模式。 天真的解决方案似乎是这样的:

my $match = "some_(\w+)_thing";
my $repl = "no_$1_stuff";

my $text = "some_strange_thing";
$text =~ s/$match/$repl/;
print "Result: $text\n";  

但是:

"Result: no__stuff".

怎么了?如何使用外部提供的模式获得相同的结果?

方案一:String::Substitution

使用String::Substitution:

use String::Substitution qw(gsub_modify);

my $find = 'some_(\w+)_thing';
my $repl = 'no__stuff';
my $text = "some_strange_thing";
gsub_modify($text, $find, $repl);
print $text,"\n";

The replacement string only interpolates (term used loosely) numbered match vars (like </code> or <code>). See "interpolate_match_vars" for more information.
This module does not save or interpolate $& to avoid the "considerable performance penalty" (see perlvar).

方案二:Data::Munge

这是在下面的评论中提到的解决方案。

Data::Munge的使用方法如下:

use Data::Munge;

my $find = qr/some_(\w+)_thing/;
my $repl = 'no__stuff';
my $text = 'some_strange_thing';
my $flags = 'g';
print replace($text, $find, $repl, $flags);
# => no_strange_stuff

解决方案 3:快速的'n'dirty 方法(如果替换不包含双引号并且不考虑安全性)

免责声明我提供这个解决方案,因为这个方法可以在网上找到,但没有解释它的注意事项。 不要在生产中使用它

使用这种方法,您不能使用包含 " 双引号的替换字符串,因为这相当于让编写配置文件的人直接访问代码,因此不应该暴露给 Web 用户(如 所述)。

您可以使用以下代码:

#!/usr/bin/perl
my $match = qr"some_(\w+)_thing";
my $repl = '"no__stuff"';
my $text = "some_strange_thing";
$text =~ s/$match/$repl/ee;
print "Result: $text\n";

IDEONE demo

结果:

Result: no_strange_stuff

你必须

  1. '"..."' 中声明替换,以便 </code> 可以稍后进行评估</li> <li>使用<a href="http://perldoc.perl.org/perlretut.html#Using-regular-expressions-in-Perl" rel="nofollow noreferrer"><code>/ee强制对替换中的变量进行双重计算。

A modifier available specifically to search and replace is the s///e evaluation modifier. s///e treats the replacement text as Perl code, rather than a double-quoted string. The value that the code returns is substituted for the matched substring. s///e is useful if you need to do a bit of computation in the process of replacing text.

您可以使用 qr 为正则表达式 (qr"some_(\w+)_thing") 实例化模式。

基本上与公认的解决方案相同的方法,但我保留了与问题陈述相同的初始行,因为我认为这可能更容易适应更多情况:

my $match = "some_(\w+)_thing";
my $repl = "no_$1_stuff";

my $qrmatch = qr($match);
my $code = $repl;

$code =~ s/([^"\]*)(["\])/\/g;
$code = qq["$code"];

if (!defined($code)) {
  die "Couldn't find appropriate quote marks";
}

my $text = "some_strange_thing";
$text =~ s/$qrmatch/$code/ee;
print "Result: $text\n";

请注意,无论 $repl 中的内容如何,​​此方法都有效,而如果 $repl 本身包含双引号字符或以反斜杠结尾,则天真的解决方案会出现问题。

此外,假设您要循环 运行 最后的三行(或类似的内容),请确保您没有跳过 qr线。如果您跳过 qr 并仅使用 s/$match/$code/ee.

,将会产生巨大的性能差异

此外,尽管使用此解决方案执行任意代码并不像接受的解决方案那样简单,但如果它仍然可能,我也不会感到惊讶。一般来说,如果 $match$repl 来自不受信任的用户,我会避免基于 s///ee 的解决方案。 (例如,不要以此为基础构建 Web 服务)

$match$repl 由不受信任的用户提供时安全地进行这种替换,如果您的用例包括该问题,则应作为一个不同的问题提出。