1

我有一个 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

4

7 回答 7

2

如果文本永远不会包含],您可以按照之前的建议简单地使用以下内容:

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

以下允许]包含的文本,但我建议不要将其合并到更大的模式中:

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

以下允许]包含的文本,并且是最强大的解决方案:

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

例如,

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

或者

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

  • /x:忽略模式中的空格。允许添加空格以使模式可读而不改变模式的含义。记录在perlre中。
  • /g:查找所有匹配项。记录在perlop中。
  • (?0)用于使模式递归,因为链接节点必须处理花括号的任意嵌套。* /g:查找所有匹配项。记录在perlre中。
于 2012-09-04T21:18:55.290 回答
2

代码可能如下所示:

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正则表达式修饰符,它允许我广泛评论它。)

于 2012-09-04T20:48:05.140 回答
1
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
>
  • 秒/。[[(. )]].*/\1/g
  • .*[[->匹配任何字符直到[[
  • (.*)]] 在 \1 中存储字符串 "[[" 直到 "]]" 之后的任何字符
  • .*-> 匹配该行的其余部分。

然后因为我们在 \1 中有我们的数据,我们可以简单地使用它在控制台上打印。

于 2012-09-05T13:59:38.940 回答
1

您找到的答案是递归模式匹配,我认为您不需要。

  • /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"
于 2012-09-04T21:10:10.287 回答
1

我建议使用模块 Text::Balanced 中的“extract_bracketed”或“extract_delimited” - 请参见此处: http: //perldoc.perl.org/Text/Balanced.html

于 2012-09-05T06:17:48.360 回答
1
\[\[(.*)]]

\[是文字 [, ]是文字 ], .*表示每个 0 或更多字符的序列,括号中的内容是捕获组,因此您可以稍后在脚本中使用 $1 (或 $2 .. $9 取决于有多少组你有)。

把所有的东西放在一起,你会匹配两个[然后所有的东西直到最后一次出现两个连续的]

更新 在第二次阅读您的问题时,我突然感到困惑,您是否需要 [[ 和 ]] 之间的内容,还是整行 - 在这种情况下,将括号完全去掉,只需测试模式是否匹配,无需捕获.

于 2012-09-04T20:48:18.690 回答
0
my @array = $str =~ /( \{ (?: [^{}]* | (?0) )* \} )/xg;

'x' 标志意味着在正则表达式中忽略空格,以允许更易读的表达式。'g' 标志意味着结果将是从左到右的所有匹配项的列表(匹配 *g*lobally)。

表示第一组括号内的(?0)正则表达式。它是一个递归正则表达式,相当于一组规则,例如:

E := '{' ( NoBrace | E) '}'
NoBrace := [^{}]* 
于 2012-09-04T20:58:56.477 回答