预定义替换的复杂替换



我正在尝试在s///中使用变量。此示例代码按预期工作:

my $regex1 = "e";
my $regex2 = "2";
my @array = ("one two three", "green blue red");
$_ =~ s/$regex1/$regex2/gee foreach (@array);
print $_ foreach (@array);

但是,如果我尝试执行更复杂的正则表达式,例如:

my $regex1 = "^(w)";
my $regex2 = "u$1";

然后替换根本不起作用。我感觉 Perl 实际上是在寻找"插入符号括号反斜杠"等等,而不是将其解释为正则表达式。

您需要防止元字符的插值:

my $regex1 = '^(w)';
my $regex2 = '"u$1"';

(根据@ThisSuitIsBlackNot的评论更新(

原因是 Perl 插入双引号字符串,因此您的变量$regex1$regex2不包含您需要的内容:

my $regex1 = "^(w)";
my $regex2 = "u$1";
print "$regex1n"; # ^(w)
print "$regex2n"; # empty line

因此,替换运算符按s/^(w)//gee工作,当然找不到任何东西。

真的不想这样做,因为允许人们将Perl代码传递给你的程序,这些代码将交给eval并不是一件好事。 除了非常复杂之外,如果没有仔细检查,它还会让您陷入恶意。如果有人输入了aaa/"unlink *"则必要的/ee将删除您当前的文件夹

让我们先澄清一些事情。在s/PATTERN/REPLACEMENT/中,只有PATTERN是正则表达式。 REPLACEMENT是一个简单的字符串,计算结果就好像是用双引号计算的

因此,让我们像这样编写程序。我把你所有的字符串都放在单引号里,因为你不想使用转义序列或变量插值。我也把你的/eeg修饰符改成/g.看起来你正在喷/e,希望它可以工作,这不是编写软件的方法。

use strict;
use warnings 'all';
my $regex       = 'e';
my $replacement = '2';
my @array = ('one two three', 'green blue red');
s/$regex/$replacement/g for @array;
print "$_n" for @array;

输出

on2 two thr22
gr22n blu2 r2d

现在您想将其更改为

my $regex       = "^(w)";
my $replacement = "u$1";

这就是为什么我扔掉了你的双引号。Perl 尝试编译"^(w)",并将w视为它无法识别的转义序列,因此您可以得到

Unrecognized escape w passed through

它假设你的意思只是w.除非你想像"^(\w)"那样转义反斜杠,否则你需要单引号来表示字符串^(w)

类似的事情也适用于$replacement。 = "\u$1";

你将看到的第一件事是 Perl 试图将 $1 的当前值插入到双引号字符串中。它目前未定义,因此您可以得到

Use of uninitialized value $1 in ucfirst

但即便如此,它还是有义务并使用空字符串进行$1,然后将其大写为您离开......空字符串

所以现在你已经设置

$regex       = '^(w)';
$replacement = '';

所以什么都行不通也就不足为奇

让我们再做一次你的程序,但这次使用单引号,这样就不会搞砸任何东西

use strict;
use warnings 'all';
my $regex       = '^(w)';
my $replacement = 'u$1';
my @array = ('one two three', 'green blue red');
s/$regex/$replacement/g for @array;
print "$_n" for @array;

现在$regex真的是^(w)$replacement真的是u$1.可能出现什么问题?

它工作正常。我们得到

u$1ne two three
u$1reen blue red

这正是我们要求的

但是现在您的/e修饰符很有用。单个/eREPLACEMENT作为表达式进行评估。如果我们想在其中粘贴$1 . 'xxx'或类似内容,这将很有用,但由于表达式是$replacement,我们根本没有任何优势:表达式$replacement与插值$replacement相同

我们需要另一个/e吗?这将在第一个/e的结果上调用eval,所以我们要求eval 'u$1',并且不会编译,因为u$1不是一个可行的Perl程序,所以eval返回undef,我们得到

Use of uninitialized value in substitution iterator

解决方案是将$replacement变成可编译的程序。用双引号括起来,就像"u$1"一样,它会变成一个非常短的Perl程序,它返回当前值$1,第一个字符大写

我们需要为该字符串设置$replacement,包括双引号,并避免像以前一样处理转义字符和$1。如果我写

my $replacement = '"u$1"';

然后我得到的字符串"u$1"包括双引号

现在让我们试试

use strict;
use warnings 'all';
my $regex       = '^(w)';
my $replacement = '"u$1"';
my @array = ('one two three', 'green blue red');
s/$regex/$replacement/eeg for @array;
print "$_n" for @array;

输出

One two three
Green blue red

正如我所说,你真的不想这样做!

最新更新