簡體   English   中英

使用外部參數的 Perl 正則表達式替換

[英]Perl regex substitution using external parameters

考慮以下示例:

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

它打印

“結果:no_奇怪的東西”

到現在為止還挺好。

現在,我需要從外部來源(用戶輸入、配置文件等)獲取匹配和替換模式。 天真的解決方案似乎是這樣的:

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

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

然而:

“結果:no_ $1 _stuff”。

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

解決方案 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:快速的'n'dirty 方式(如果替換不包含雙引號並且不考慮安全性)

免責聲明我提供了這個解決方案,因為這種方法可以在網上找到,但沒有解釋它的警告。 不要在生產中使用它

使用這種方法,您不能有一個包含"雙引號的替換字符串,並且由於這相當於將編寫配置文件的任何人直接代碼訪問,它不應該暴露給 Web 用戶(如Daniel馬丁)。

您可以使用以下代碼:

#!/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 演示

結果:

Result: no_strange_stuff

你必須

  1. '"..."'聲明替換,以便稍后可以評估$1
  2. 使用/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 ,它將產生巨大的性能差異。

此外,即使使用此解決方案執行任意代碼並不像使用已接受的解決方案那么簡單,但如果它仍然可能,我也不會感到驚訝。 一般來說,如果$match$repl來自不受信任的用戶,我會避免基於s///ee解決方案。 (例如,不要以此為基礎構建 Web 服務)

$match$repl由不受信任的用戶提供時,如果您的用例包含該問題,則應將其作為不同的問題來安全地進行此類替換。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM