2010-11-03 219 views
0

任何人都可以解释正则表达式在正则表达式保存在变量中时的正则表达式文本替换吗?我正在尝试处理一些文本,实际上是Clearcase配置规范,并在我去的时候替换文本。替换的规则保存在一个哈希数组中,这些哈希有正则表达式匹配和要替换的文本。Perl正则表达式变量和匹配模式替换

输入文字看起来财产以后这样的:

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

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

+3

不要使用ddoouubbllee slackbashed字符串的正则表达式,然后编译它所有的时间。只需直接创建散列值'qr //'字符串即可。不要在替换的RHS上使用'\\ 1'!请摆脱那些丑陋的LTS字符串。 – tchrist 2010-11-03 17:07:26

+2

我相信有人会愿意阅读这篇文章。与此同时,请自己和任何必须阅读的人编写代码并在'perldoc perlreref'中查找'\ Q'。 – 2010-11-03 17:08:36

+0

公平评论。这段代码在我一直在尝试的过程中经过了几次修改 - 我删除了qr以控制什么是逃脱和什么不是。除了$ 1 \ 1替换之外,您可以相信我的正则表达式正常工作。 – 0xDEADBEEF 2010-11-03 17:09:10

回答

2

没有替换表达式的编译。所以,你唯一可以做的事情是高管或与e标志EVAL它:

if($test =~ s/$regex/eval qq["$hash->{subs}"]/e) { #... 

在替换字符串改变\\1\$1后为我工作。

s/$regex/$hash->{subs}/ 

只替换存储在$hash->{subs}完全取代的字面值匹配的部件。为了得到替换工作,你必须强制Perl字符串评估作为一个字符串,这样就意味着你甚至要回加dquotes为了得到你正在寻找插值行为(因为它们是不是字符串的一部分)

但是,这是一种笨拙,所以我改变了更换表情到潜艇:

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

只是最后一个注释:"\.\.\."没有做你认为它做的事。你只是在正则表达式中结束了'...',它匹配任何三个字符。

+0

非常感谢您的答案 - 两个版本都运行良好,您的答案非常丰富。我用你稍微优雅的'sub'版本去了,但是在正则表达式有多个匹配变量的情况下没有参数。只是一个小点 - 是不是替代正则表达式缺少最终评估e? s/$ regex/$ hash - > {subs} - >()/ e适用于我。 – 0xDEADBEEF 2010-11-04 07:53:38

3

正则表达式中的替换字符串只进行一次评估,将$hash->{subs}转换为其字符串。您需要再次对其进行评估以插入其内部变量。您可以将e修饰符添加到正则表达式的末尾,该修饰符告诉Perl通过eval运行替代,该替换可以执行第二次插值以及其他操作。您可以应用多个e标志进行多次评估(如果您有需要的问题)。由于tchrist有帮助地指出,在这种情况下,您需要ee,因为第一个eval只会扩展变量,第二个需要展开扩展中的变量。

你可以在perlop about the s operator找到更多的细节。

+0

埃里克,请注意,替代品上的RHS是'$ foo'与没有'/ e'是相同的,这就是为什么这种东西总是需要'/ ee'来代替。 – tchrist 2010-11-03 17:38:56

+0

是真的,就像'“$ foo”'和'$ foo'一样,回答更新 – 2010-11-03 17:42:30