Perl: как извлечь строку между скобками

У меня есть файл в текстовом формате moinmoin:

* [[  Virtualbox Guest Additions]] (2011/10/17 15:19)
* [[  Abiword Wordprocessor]] (2010/10/27 20:17)
* [[  Sylpheed E-Mail]] (2010/03/30 21:49)
* [[   Kupfer]] (2010/05/16 20:18)

Все слова между '[[' и ']]' являются кратким описанием записи. Мне нужно извлечь всю запись, а не каждое слово в отдельности.

Я нашел здесь ответ на аналогичный вопрос: https://stackoverflow.com/a/2700749/819596, но могу » Я не понимаю ответ: "my @array = $str =~ /( \{ (?: [^{}]* | (?0) )* \} )/xg;"

Все, что работает, будет принято, но очень помогут пояснения, например: что делает (?0) или /xg.


person marinara    schedule 04.09.2012    source источник
comment
спасибо за ответы, собираюсь вздремнуть и попробовать ответы!   -  person marinara    schedule 05.09.2012


Ответы (7)


Код, вероятно, будет выглядеть так:

use warnings; 
use strict;

my @subjects; # declaring a lexical variable to store all the subjects
my $pattern = qr/ 
  \[ \[    # matching two `[` signs
  \s*      # ... and, if any, whitespace after them
  ([^]]+) # starting from the first non-whitespace symbol, capture all the non-']' symbols
  ]]
/x;

# main processing loop:
while (<DATA>) { # reading the source file line by line
  if (/$pattern/) {      # if line is matched by our pattern
    push @subjects, $1;  # ... push the captured group of symbols into our array
  }
}
print $_, "\n" for @subjects; # print our array of subject line by line

__DATA__
* [[  Virtualbox Guest Additions]] (2011/10/17 15:19)
* [[  Abiword Wordprocessor]] (2010/10/27 20:17)
* [[  Sylpheed E-Mail]] (2010/03/30 21:49)
* [[   Kupfer]] (2010/05/16 20:18)

Как я вижу, то, что вам нужно, можно описать так: в каждой строке файла попробуйте найти эту последовательность символов ...

[[, an opening delimiter, 
then 0 or more whitespace symbols,
then all the symbols that make a subject (which should be saved),
then ]], a closing delimiter

Как видите, это описание вполне естественно переводится в регулярное выражение. Единственное, что, вероятно, не нужно, - это модификатор /x regex, который позволил мне подробно его прокомментировать. )

person raina77ow    schedule 04.09.2012

Если текст никогда не будет содержать ], вы можете просто использовать следующее, как было рекомендовано ранее:

/\[\[ ( [^\]]* ) \]\]/x

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

/\[\[ ( .*? ) \]\]/x

Следующее позволяет использовать ] в содержащемся тексте и является наиболее надежным решением:

/\[\[ ( (?:(?!\]\]).)* ) \]\]/x

Например,

if (my ($match) = $line =~ /\[\[ ( (?:(?!\]\]).)* ) \]\]/x) {
   print "$match\n";
}

or

my @matches = $file =~ /\[\[ ( (?:(?!\]\]).)* ) \]\]/xg;

  • /x: игнорировать пробелы в шаблоне. Позволяет добавлять пробелы, чтобы сделать шаблон читаемым без изменения смысла шаблона. Документировано в perlre.
  • /g: Найти все совпадения. Документировано в perlop.
  • (?0) использовался, чтобы сделать шаблон рекурсивным, поскольку связанный узел должен был иметь дело с произвольным вложением фигур. * /g: Найти все совпадения. Документировано в perlre.
person ikegami    schedule 04.09.2012

\[\[(.*)]]

\[ является литералом [, ] является литералом], .* означает каждую последовательность из 0 или более символов, что-то заключенное в круглые скобки является группой захвата, поэтому вы можете получить к нему доступ позже в своем скрипте с помощью $ 1 (или $ 2 .. $ 9 в зависимости от сколько групп у вас есть).

Собрав все вместе, вы найдете два [, а затем все до последнего появления двух последовательных ]

Обновить При втором чтении вашего вопроса я неожиданно запутался, нужно ли вам содержимое между [[и]] или всю строку - в этом случае полностью опустите круглые скобки и просто проверьте, совпадения с шаблоном, нет необходимости захватывать.

person pulven    schedule 04.09.2012

Ответ, который вы нашли, касается рекурсивного сопоставления с образцом, который, я думаю, вам не нужен.

  • / x позволяет использовать бессмысленные пробелы и комментарии в регулярном выражении.

  • / g запускает регулярное выражение по всей строке. Без него длится только до первого матча.

  • / xg - это / x и / g вместе.

  • (? 0) снова запускает само регулярное выражение (рекурсия)

Если я понимаю, вам нужно что-то вроде этого:

$text="* [[  Virtualbox Guest Additions]] (2011/10/17 15:19)
* [[  Abiword Wordprocessor]] (2010/10/27 20:17)
* [[  Sylpheed E-Mail]] (2010/03/30 21:49)
* [[   Kupfer]] (2010/05/16 20:18)
";

@array=($text=~/\[\[([^\]]*)\]\]/g);
print join(",",@array);

# this prints "  Virtualbox Guest Additions,  Abiword Wordprocessor,  Sylpheed E-Mail,   Kupfer"
person lalborno    schedule 04.09.2012

Я бы рекомендовал использовать «extract_bracketed» или «extract_delimited» из модуля Text :: Balanced - см. Здесь: http://perldoc.perl.org/Text/Balanced.html

person Helen Craigman    schedule 05.09.2012

perl -pe 's/.*\[\[(.*)\]\].*/\1/g' temp

протестировано ниже:

> cat temp
        * [[  Virtualbox Guest Additions]] (2011/10/17 15:19)
        * [[  Abiword Wordprocessor]] (2010/10/27 20:17)
        * [[  Sylpheed E-Mail]] (2010/03/30 21:49)
        * [[   Kupfer]] (2010/05/16 20:18)
>
> perl -pe 's/.*\[\[(.*)\]\].*/\1/g' temp
  Virtualbox Guest Additions
  Abiword Wordprocessor
  Sylpheed E-Mail
   Kupfer
>
  • s/.[[(.)]].*/\1/g
  • . * [[-> соответствует любому символу до [[
  • (. *)]] сохранить любой символ после строки "[[" до "]]" в \ 1
  • . * -> соответствует остальной части строки.

тогда, поскольку у нас есть данные в \ 1, мы можем просто использовать их для печати на консоли.

person Vijay    schedule 05.09.2012

my @array = $str =~ /( \{ (?: [^{}]* | (?0) )* \} )/xg;

Флаг 'x' означает, что пробелы в регулярном выражении игнорируются, чтобы выражение было более читабельным. Флаг 'g' означает, что результатом будет список всех совпадений слева направо (совпадение * g * локально).

(?0) представляет собой регулярное выражение внутри первой группы круглых скобок. Это рекурсивное регулярное выражение, эквивалентное набору правил, таких как:

E := '{' ( NoBrace | E) '}'
NoBrace := [^{}]* 
person chepner    schedule 04.09.2012