Perl正则表达式变量和匹配模式替换

时间:2010-11-03 17:01:39

标签: regex perl substitution

当正则表达式保存在变量中时,是否可以解释正则表达式文本替换?我正在尝试处理一些文本,实际上是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";
                }
         }
}

我错过了什么?提前谢谢。

2 个答案:

答案 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"
         ; 
}

最后一个注意事项:"\.\.\."并没有按照您的想法行事。你刚刚在正则表达式中使用了'...',它匹配任意三个字符。