Как сохранить пробелы при сопоставлении и замене нескольких слов в Perl?

Допустим, у меня есть исходный текст:

here is some text that has a substring that I'm interested in embedded in it.

Мне нужно, чтобы текст соответствовал его части, скажем: "has a substring".

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

has a
substring

or

has  a substring

и/или исходный текст может быть:

here is some
text that has
a substring that I'm interested in embedded in it.

Что мне нужно, чтобы моя программа выводила:

here is some text that [match starts here]has a substring[match ends here] that I'm interested in embedded in it.

Мне также нужно сохранить образец пробела в оригинале и просто добавить к нему начальный и конечный маркеры.

Любые идеи о способе использования регулярных выражений Perl, чтобы это произошло? Я пытался, но в итоге ужасно запутался.


person singingfish    schedule 15.09.2009    source источник
comment
Вы предполагаете, что подстрока является статической, или что вы будете иметь дело с переменной короткой строкой и переменной длинной строкой? Все ответы предполагают первое, но последнее представляет собой гораздо более интересную проблему. :)   -  person Ether    schedule 15.09.2009
comment
@Ether: На самом деле это последнее, но мой набор данных конечен и известен, поэтому в этом случае я могу разбить большую проблему на маленькие проблемы.   -  person singingfish    schedule 16.09.2009


Ответы (5)


Прошло некоторое время с тех пор, как я использовал регулярные выражения Perl, но как насчет:

$match = s/(has\s+a\s+substring)/[$1]/ig

Это зафиксирует ноль или более пробелов и символов новой строки между словами. Он завершит все совпадение скобками, сохранив при этом исходное разделение. Это не автоматически, но это работает.

Вы можете играть с этим в игры, например, взять строку "has a substring" и выполнить преобразование, чтобы сделать ее "has\s*a\s*substring", чтобы сделать это немного менее болезненным.

РЕДАКТИРОВАТЬ: Включены комментарии ysth о том, что метасимвол \s соответствует новой строке, и исправления hobbs для моего использования \s.

person David Andres    schedule 15.09.2009
comment
\s включает \r и \n, поэтому просто \s совпадает с вашим [\s\r\n] - person ysth; 15.09.2009
comment
Я бы предложил \s+ вместо \s*, если вы не хотите сопоставлять hasasubstring, что, я не думаю, было одним из вариантов пробелов, которые имел в виду OP. - person hobbs; 15.09.2009
comment
hobbs там правильно, это \s+. Увидев ответы, но не протестировав их с моими довольно сложными наборами файлов данных, я думаю, что моя проблема в основном заключалась в том, чтобы не разделить проблему на достаточно маленькие кусочки. - person singingfish; 15.09.2009

Этот шаблон будет соответствовать строке, которую вы ищете:

(has\s+a\s+substring)

Итак, когда пользователь вводит строку поиска, замените любой пробел в строке поиска на \s+, и вы получите свой шаблон. Просто замените каждое совпадение на [match starts here]$1[match ends here], где $1 — это совпадающий текст.

person Doug Hays    schedule 15.09.2009

В регулярных выражениях вы можете использовать + для обозначения «один или несколько». Итак, что-то вроде этого

/has\s+a\s+substring/

соответствует has, за которым следует один или несколько пробельных символов, за которым следует a, за которым следует один или несколько пробельных символов, за которыми следует substring.

Объединив это с оператором подстановки, вы можете сказать:

my $str = "here is some text that has     a  substring that I'm interested in embedded in it.";
$str =~ s/(has\s+a\s+substring)/\[match starts here]$1\[match ends here]/gs;

print $str;

И вывод:

here is some text that [match starts here]has     a  substring[match ends here] that I'm interested in embedded in it.
person friedo    schedule 15.09.2009

Многие предлагали использовать \s+ для соответствия пробелам. Вот как это сделать автоматически:

my $original = "here is some text that has a substring that I'm interested in embedded in it.";
my $search = "has a\nsubstring";

my $re = $search;
$re =~ s/\s+/\\s+/g;

$original =~ s/\b$re\b/[match starts here]$&[match ends here]/g;

print $original;

Выход:

here is some text that [match starts here]has a substring[match ends here] that I'm interested in embedded in it.

Возможно, вы захотите экранировать любые метасимволы в строке. Если кому интересно, могу добавить.

person Markus Jarderot    schedule 15.09.2009

Это пример того, как вы можете это сделать.

#! /opt/perl/bin/perl
use strict;
use warnings;

my $submatch = "has a\nsubstring";

my $str = "
here is some
text that has
a substring that I'm interested in, embedded in it.
";

print substr_match($str, $submatch), "\n";

sub substr_match{
  my($string,$match) = @_;

  $match =~ s/\s+/\\s+/g;

  # This isn't safe the way it is now, you will need to sanitize $match
  $string =~ /\b$match\b/;
}

В настоящее время это делает все, чтобы проверить переменную $match на наличие небезопасных символов.

person Brad Gilbert    schedule 15.09.2009
comment
что вы имеете в виду под санацией? - person singingfish; 16.09.2009