使用Perl正则表达式删除字符串中重复的连续子字符串

4
我会尽力帮助您进行翻译。以下是需要翻译的内容:

我尝试在这个特定问题上进行搜索,但我得到的都是要么删除重复行,要么删除由分隔符分隔的重复字符串。

我的问题略有不同。我有一个字符串,例如

    "comp name1 comp name2 comp name2 comp name3" 

我希望删除重复的comp name2并仅返回

    "comp name1 comp name2 comp name3" 

他们不是连续重复的单词,而是连续重复的子字符串。是否有一种使用正则表达式解决此问题的方法?

如果你有 "comp name1 comp name2 comp name2 comp name3 comp name4 comp name2",输出会是什么? - kurumi
嗨@kurumi,我只对连续重复感兴趣。因此,第二个(或输入中的第三个)comp name2将保持不变。 - Rasika
需要使用正则表达式吗?在我看来,字符串方法可能更好。 - Justin Morgan
5个回答

8
s/(.*)\1/$1/g

请注意,此正则表达式的运行时间与字符串长度成二次关系。


我知道时间复杂度的影响。在我的场景中,这些字符串相对较短(最多约100个字符),不会花费太长时间。 - Rasika
@btilly:如果我有连续重复的行,那么对于行而不是字符串的同样问题怎么办? - unkaitha
@unkaitha: perl -ne 'print unless $seen{$_}++' file.txt > no_dupe_lines.txt - btilly

3
这对我来说可行(MacOS X 10.6.7,Perl 5.13.4):
use strict;
use warnings;

my $input = "comp name1 comp name2 comp name2 comp name3" ;
my $output = "comp name1 comp name2 comp name3" ;

my $result = $input;
$result =~ s/(.*)\1/$1/g;

print "In:   <<$input>>\n";
print "Want: <<$output>>\n";
print "Got:  <<$result>>\n";

关键点在于匹配中的 '\1'。

@btilly的解决方案稍有变化。谢谢,但我必须选择另一个,因为他先到了。 - Rasika

2
为避免在术语内删除重复字符(例如,comm1 -> com1),请在正则表达式中使用\b来表示括号.*。
s/(\b.*\b)\1/$1/g

1
如果您需要在线性时间内运行某些内容,您可以使用split函数将字符串拆分并遍历列表:
#!/usr/bin/perl                                                                                                                                                                                       

use strict;
use warnings;

my $str = "comp name1 comp name2 comp name2 comp name3";
my @elems = split("\\s", $str);
my $prevComp;
my $prevFlag = -1;
foreach my $elemIdx (0..(scalar @elems - 1)) {
    if ($elemIdx % 2 == 1) {
        if (defined $prevComp) {
            if ($prevComp ne $elems[$elemIdx]) {
                print " $elems[$elemIdx]";
                $prevFlag = 0;
            }
            else {
                $prevFlag = 1;
            }
        }
        else {
            print " $elems[$elemIdx]";
        }
        $prevComp = $elems[$elemIdx];
    }
    elsif ($prevFlag == -1) {
        print "$elems[$elemIdx]";
        $prevFlag = 0;
    }
    elsif ($prevFlag == 0) {
        print " $elems[$elemIdx]";
    }
}
print "\n";

可能有点不规范,但应该会运行得更快。


1

我从未使用过支持此功能的语言,但既然您正在使用Perl...

请前往此处...并查看此部分....

有用的示例:检查重复单词

在编辑文本时,"the the"等重复单词很容易出现。在您的文本编辑器中使用正则表达式\b(\w+)\s+\1\b,您可以轻松找到它们。要删除第二个单词,只需键入\1作为替换文本,然后单击“替换”按钮即可。


2
请,请,请。不要称这种语言为“pearl”。它是“Perl”,可执行文件是“perl”。 - btilly
1
@btilly:已为他修复 - 我完全同意。此外,问题并不是关于简单的“重复词语”;而是关于“重复短语”,其中短语可能由多个单词组成。你给出的答案可以扩展到达到所需的答案,但是…… - Jonathan Leffler
我在搜索中找到了这个,但它只适用于重复的单词而不是字符串。我的子字符串中有单词边界,所以这个方法不适用。 - Rasika
是的,我应该使用“双引号”而不是子字符串。 - Rasika

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接