Perl正则表达式与LHS中的分组

时间:2013-07-01 18:27:31

标签: regex perl

如何匹配下一行?

sometext_TEXT1.yyy-TEXT1.yyy
anothertext_OTHER.yyy-MAX.yyy

要从最后删除- repetative.text,但只有重复时才会删除。

sometext_TEXT1.yyy
anothertext_OTHER.yyy-MAX.yyy

我的尝试

use strictures;
my $text="sometext_TEXT1.xxx-TEXT1.xxx";
$text =~ s/(.*?)(.*)(\s*-\s*$2)/$1$2/;
print "$text\n";

打印

Use of uninitialized value $2 in regexp compilation at a line 3.

换句话说,为下一个split + match ...

寻找更好的解决方案
while(<DATA>) {
        chomp;

        my($first, $second) = split /\s*-\s*/;
        s/\s*-\s*$second$// if ( $first =~ /$second$/ );

        print "$_\n";
}
__DATA__
sometext_TEXT1.yyy-TEXT1.yyy
anothertext_OTHER.yyy-MAX.yyy

2 个答案:

答案 0 :(得分:2)

$text =~ s/(.*?)(.*)(\s*-\s*$2)/$1$2/;

这个正则表达式有各种各样的问题,但是正确的道路。

  1. 使用\2(或更好:\g2\g{-1})或其他内容来引用捕获组的内容。执行Perl语句时会插入$2变量。那时,$2未定义,因为之前没有匹配。由于未初始化,您会收到警告。即使已定义,也会在编译期间修复模式。

  2. 您定义了三个捕获组,但只需要一个。 \K eep指令有一个技巧:让正则表达式引擎忘记以前匹配的文本,这样它就不会受到替换的影响。也就是说,s/(foo)b/$1/相当于s/foo\Kb//。效果类似于可变长度的后视。

  3. (.*?)(.*)部分是一个回溯的噩梦。我们可以通过添加更多条件来降低您的匹配成本,例如:通过在开始和结束处锚定模式。使用上述修改,我们现在有s/^.*?(.*)\K\s*-\s*\g1$//。但是第二个想法,我们可以删除^.*?因为这描述了正则表达式引擎所做的事情!

  4. 简短测试:

    while(<DATA>) {
      s/(.*)\K\s*-\s*\g1$//;
      print;
    }
    __DATA__
    sometext_TEXT1.yyy-TEXT1.yyy
    anothertext_OTHER.yyy-MAX.yyy
    

    输出:

    sometext_TEXT1.yyy
    anothertext_OTHER.yyy-MAX.yyy
    

    关于split解决方案的几句话:这也会缩短行

    sometext_TEXT1xyyy - 1.xyyy
    

    因为当您将变量插入到正则表达式中时,内容不是字面上匹配的。相反,它们被解释为一种模式(.匹配任何非换行代码点)!您可以通过使用\Q...\E转义引用所有元字符来避免这种情况:

    s/\s*-\s*\Q$second\E$// if $first =~ /\Q$second\E$/;
    

答案 1 :(得分:1)

使用$2 Perl会尝试插入该变量,但只有匹配完成后才能设置变量。你想要的是一个反向引用,你需要使用\2

$text =~ s/(.*?)(.*)(\s*-\s*\2)/$1$2/;

请注意,评估替换部件时,$1$2已设置,可按预期进行插补。您还可以使用:

使模式更简洁(并且可能更高效)
$text =~ s/(.*)\s*-\s*\2/$1/;

如果它是任意的,则无需匹配初始部分(.*?),无论如何您只需将其写回。您可能想要做的是将模式锚定到字符串的末尾:

$text =~ s/(.*)\s*-\s*\1$/$1/;

否则(初次尝试或我的尝试),您将something-thingelse变为somethingelse