使用外部参数的 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
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";
结果:
Result: no_strange_stuff
你必须
- 在
'"..."'
中声明替换,以便 </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
由不受信任的用户提供时安全地进行这种替换,如果您的用例包括该问题,则应作为一个不同的问题提出。
考虑以下示例:
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
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";
结果:
Result: no_strange_stuff
你必须
- 在
'"..."'
中声明替换,以便</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
由不受信任的用户提供时安全地进行这种替换,如果您的用例包括该问题,则应作为一个不同的问题提出。