当正则表达式保存在变量中时,是否可以解释正则表达式文本替换?我正在尝试处理一些文本,实际上是Clearcase配置规范,并在我去的时候替换文本。替换的规则保存在哈希数组中,哈希具有要匹配的正则表达式和要替换的文本。
输入文字看起来像这样:
element /my_elem/releases/... VERSION_STRING.020 -nocheckout
大多数替换只是删除包含特定文本字符串的行,这很好。在某些情况下,我想替换文本,但重新使用VERSION_STRING文本。我尝试在替换表达式中使用$ 1,但它不起作用。 $ 1获取匹配中的版本字符串,但替换$ 1在替换中不起作用。
在这些情况下,输出应如下所示:
element -directory /my_elem/releases/... VERSION_STRING.020 -nocheckout
element /my_elem/releases/.../*.[ch] VERSION_STRING.020 -nocheckout
即。一行输入变为两个输出,版本字符串已被重复使用。
代码看起来像这样。首先是正则表达式和替换:
my @Special_Regex = (
{ regex => "\\s*element\\s*\/my_elem_removed\\s*\/main\/\\d+\$", subs => "# Line removed" },
{ regex => "\\s*element\\s*\/my_elem_changed\/releases\/\.\.\.\\s*\(\.\*\$\)",
subs => "element \-directory \/my_elem\/releases\/\.\.\. \\1\nelement \/my_elem\/releases\/\.\.\.\/\*\.\[ch\] \\1" }
);
在第二个正则表达式中,变量$ 1在部分(。* \ $)中定义,这是正常的。但是,subs表达式不能替代它。
foreach my $line (<INFILE>)
{
chomp($line);
my $test = $line;
foreach my $hash (@Special_Regex)
{
my $regex = qr/$hash->{regex}/is;
if($test =~ s/$regex/$hash->{subs}/)
{
print "$test\n";
print "$line\n";
print "$1\n";
}
}
}
我错过了什么?提前谢谢。
答案 0 :(得分:3)
正则表达式中的替换字符串只会被评估一次,这会将$hash->{subs}
转换为其字符串。您需要再次评估它以插入其内部变量。您可以将e
修饰符添加到正则表达式的末尾,该正则表达式告诉Perl通过eval
运行替换,这可以执行第二次插值等。您可以应用多个e
标志来多次评估(如果您遇到需要它的问题)。正如 tchrist 有用地指出的那样,在这种情况下,你需要ee
,因为第一个eval只会展开变量,第二个就需要扩展变量中的变量。
您可以在perlop
about the s
operator中找到更多详细信息。
答案 1 :(得分:2)
没有替换表达式的编译。所以你唯一能做的就是用e
标志来执行或评估它:
if($test =~ s/$regex/eval qq["$hash->{subs}"]/e ) { #...
在替换字符串中将\\1
更改为\$1
后,为我工作。
s/$regex/$hash->{subs}/
仅将匹配的部分替换为$hash->{subs}
中存储的 literal 值作为的完整替换。为了使替换工作,您必须强制Perl将字符串计算为字符串,这意味着您甚至必须重新添加dquotes才能获得您正在寻找的插值行为for(因为它们不是字符串的一部分。)
但这有点笨拙,所以我将替换表达式改为subs:
my @Special_Regex
= (
{ regex => qr{\s*element\s+/my_elem_removed\s*/main/\d+$}
, subs => sub { '#Line removed' }
}
, { regex => qr{\s*element\s+/my_elem_changed/releases/\.\.\.\s*(.*$)}
, subs => sub {
return "element -directory /my_elem/releases/... $1\n"
. "element /my_elem/releases/.../*.[ch] $1"
;
}
}
);
我摆脱了一堆你不必在替换表达式中逃脱的东西。由于您要做的是将$1
的值插入到替换字符串中,子例程简单地。并且因为$1
在匹配其他内容之前是可见的,所以当我们运行此代码时它将是正确的值。
所以现在更换看起来像:
s/$regex/$hash->{subs}->()/e
当然,将其传递 $1
会使其更具防弹性,因为您不依赖全局$1
:
s/$regex/$hash->{subs}->( $1 )/e
当然,你会像这样改变子:
subs => sub {
my $c1 = shift;
return "element -directory /my_elem/releases/... $c1\n"
. "element /my_elem/releases/.../*.[ch] $c1"
;
}
最后一个注意事项:"\.\.\."
并没有按照您的想法行事。你刚刚在正则表达式中使用了'...'
,它匹配任意三个字符。