请考虑以下示例:
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”。
怎么了?如何通过外部提供的模式获得相同的结果?
解决方案1: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";
结果:
Result: no_strange_stuff
你必须
'"..."'
中声明替换,以便稍后可以评估$1
/ee
强制对替换中的变量进行双重评估。专门用于搜索和替换的修饰符是
s///e
评估修饰符。s///e
将替换文本视为Perl代码,而不是双引号字符串。代码返回的值将替换匹配的子字符串。如果您需要在替换文本的过程中进行一些计算,s///e
非常有用。
您可以使用qr
来实例化正则表达式(qr"some_(\w+)_thing"
)的模式。
基本上与接受的解决方案相同的方法,但我保持初始行与问题陈述相同,因为我认为这可能使更容易适应更多情况:
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
由不受信任的用户提供时,安全地进行这种替换应该被问为一个不同的问题,如果您的用例包含该问题。