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

问题描述 投票:6回答:2

请考虑以下示例:

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

它打印

“结果: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";  

然而:

“结果:没有$ 1stuff”。

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

regex perl substitution
2个回答
9
投票

解决方案1:String::Substitution

使用String::Substitution包:

use String::Substitution qw(gsub_modify);

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

替换字符串仅插入(松散使用的术语)编号匹配变量(如$1${12})。有关更多信息,请参阅"interpolate_match_vars"。 此模块不保存或插入$&以避免“相当大的性能损失”(请参阅​​perlvar)。

解决方案2:Data::Munge

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

Data::Munge可以通过以下方式使用:

use Data::Munge;

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

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

免责声明:我提供此解决方案,因为这种方法可以在网上找到,但其解释并未解释。不要在生产中使用它。

使用这种方法,您不能拥有包含"双引号的替换字符串,因为这相当于处理编写配置文件直接代码访问权限的人,因此不应向Web用户公开(如Daniel Martin所述) )。

您可以使用以下代码:

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

IDEONE demo

结果:

Result: no_strange_stuff

你必须

  1. '"..."'中声明替换,以便稍后可以评估$1
  2. 使用/ee强制对替换中的变量进行双重评估。

专门用于搜索和替换的修饰符是s///e评估修饰符。 s///e将替换文本视为Perl代码,而不是双引号字符串。代码返回的值将替换匹配的子字符串。如果您需要在替换文本的过程中进行一些计算,s///e非常有用。

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


2
投票

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

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

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

$code =~ s/([^"\\]*)(["\\])/$1\\$2/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,它将产生巨大的性能差异。

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

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

© www.soinside.com 2019 - 2024. All rights reserved.