Регулярное выражение Perl, удаляющее повторяющиеся последовательные подстроки в строке

#regex #perl #substring

#регулярное выражение #perl #подстрока

Вопрос:

Я попытался выполнить поиск по этой конкретной проблеме, но все, что я получаю, это либо удаление повторяющихся строк, либо удаление повторяющихся строк, где они разделены разделителем.

Моя проблема немного иная. У меня есть строка, такая как

     "comp name1 comp name2 comp name2 comp name3" 
  

где я хочу удалить повторяющееся имя компа2 и вернуть только

     "comp name1 comp name2 comp name3" 
  

Это не последовательные повторяющиеся слова, а последовательные повторяющиеся подстроки. Есть ли способ решить эту проблему с помощью регулярных выражений?

Комментарии:

1. что, если у вас есть ` «имя компонента 1, имя компонента 2, имя компонента 2, имя компонента 3, имя компонента 4, имя компонента 2″` ? каким будет результат?

2. Привет @kurumi, меня интересуют только последовательные повторы. Таким образом, второе (или третье во входных данных) имя компонента2 останется нетронутым.

3. Обязательно ли это должны быть регулярные выражения? Строковые методы были бы лучше для этого, ИМХО.

Ответ №1:

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

Имейте в виду, что время выполнения этого регулярного выражения квадратично длине строки.

Комментарии:

1. Я осознаю временную сложность. В моем случае это довольно короткие строки (максимум ~ 100 символов), которые не заняли бы так много времени.

2. @btilly : как насчет той же проблемы со строками, а не со строками ????.. Если у меня есть последовательные повторяющиеся строки??

3. @unkaitha: perl -ne 'print unless $seen{$_} ' file.txt > no_dupe_lines.txt

Ответ №2:

Это работает для меня (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’ в сопоставлении.

Комментарии:

1. Небольшое изменение решения @btilly. Спасибо, но придется использовать другой вариант, поскольку он был первым.

Ответ №3:

Чтобы избежать удаления повторяющихся символов внутри скобки терминов (например, comm1 -> com1) .* в регулярном выражении с помощью b.

 s/(b.*b)1/$1/g
  

Ответ №4:

Я никогда не работаю с языками, которые поддерживают это, но поскольку вы используете Perl …

Перейдите сюда .. и посмотрите этот раздел….

Полезный пример: проверка на удвоение слов

При редактировании текста легко появляются удвоенные слова, такие как «the the». Используя regex b(w )s 1b в вашем текстовом редакторе, вы можете легко их найти. Чтобы удалить второе слово, просто введите 1 в качестве заменяющего текста и нажмите кнопку Заменить.

Комментарии:

1. Пожалуйста, пожалуйста, пожалуйста. Не называйте язык «pearl». Это «Perl», а исполняемый файл — «perl».

2. @btilly: исправлено для него — я согласен на 100%. Кроме того, вопрос не в простых «удвоенных словах»; речь идет о «удвоенных фразах», где фраза может состоять более чем из одного слова. Приведенный вами ответ можно расширить, чтобы получить требуемый ответ, но …

3. Я нашел это в своих поисках, но это только для повторяющихся слов, а не для строк. В моих подстроках есть границы слов, поэтому это не работает.

4. Да, мне следовало использовать «двойные фразы» вместо подстрок.

Ответ №5:

Если вам нужно что-то, выполняющееся в линейное время, вы можете 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";
  

Возможно, грязно, но должно выполняться быстрее.