Perl - 提取所有正则表达式匹配

时间:2015-01-02 21:45:46

标签: regex perl

$regex   = "[M]?[VI]?[A]?[R]?[G]?[D]?[LM]?[G]?[IVMAL]?[E]?";
$text = "VMVARGDLGVE";

if (@matched = $text =~ /$regex/g) {
    $no_of_match = scalar @matched;
    print "No of match: ", $no_of_match, "\n";
    foreach my $i (@matched) {
        print $i, "\n";
    }
}    

该程序生成输出如下: 不匹配:3 VMV ARGDLGVE

我期待这个程序输出如下内容: V VM VMV MV MVA MVAR MVARG MVARGD MVARGDL MVARGDLG MVARGDLGV MVARGDLGVE ....

我试图将所有可能的匹配作为输出。如何获得所有可能的匹配?

3 个答案:

答案 0 :(得分:1)

正则表达式匹配并不像那样工作。如果您在找到匹配项后再次搜索,则会从该匹配项的结尾再次开始搜索 。所以它没有找到重叠的匹配。

我假设你在Perl 5工作。我相信在Perl 6中有一些方法可以做到这一点。这真的是一种不同的语言。我不知道任何其他语言可以找到你想要的重叠正则表达式匹配。

它找到的第一个匹配项是开头的子字符串VMV。然后它从它停止的位置开始搜索,并找到匹配ARGDLGVE。然后它再次从它停止的地方开始尝试,这个阶段在字符串的末尾。所以它在末尾找到空子串作为匹配。 (请注意,你的正则表达式与空字符串匹配。)禁止正则表达式再次找到相同的空字符串,因为这会导致无限循环,因此它会停止搜索。

这段代码的重点是什么?因为我真的无法看到这样做的好方法,我希望还有其他方法来实现你的目标。您可以浏览$text的所有可能的子字符串,并使用/^$regex$/分别检查每个子字符串。根据我的计算,您将找到70个匹配项(包括重复项和空字符串)。也许你想要一个更严格的正则表达式?

答案 1 :(得分:1)

我已经重构了您的代码,以便更容易地查看正在进行的操作。

my $regex = qr/
  M?        # 1:  Match M,                  greedy, 0 or 1 times.
  [VI]?     # 2:  Match V or I,             greedy, 0 or 1 times.
  A?        # 3:  Match A,                  greedy, 0 or 1 times.
  R?        # 4:  Match R,                  greedy, 0 or 1 times.
  G?        # 5:  Match G,                  greedy, 0 or 1 times.
  D?        # 6:  Match D,                  greedy, 0 or 1 times.
  [LM]?     # 7:  Match L or M,             greedy, 0 or 1 times.
  G?        # 8:  Match G,                  greedy, 0 or 1 times.
  [IVMAL]?  # 9:  Match I, B, M, A or L,    greedy, 0 or 1 times.
  E?        # 10: Match E,                  greedy, 0 or 1 times.
/x;
my $text = "VMVARGDLGVE";

if ( my @matched = $text =~ /$regex/gx ) {
  print "No of matches: ", scalar(@matched), "\n";
  print "<<$_>>\n" foreach @matched;
}

您的正则表达式在尽可能多的方式匹配,而不会违反其NFA正则表达式引擎的规则。

  1. 其中一条规则是&#34;最左边&#34 ;;最靠近左边的子串 在目标中,将选择总匹配成功。

  2. 另一个规则是贪婪的量词将与之匹配 可能的,只会放弃他们拥有的子匹配 必须让完整的比赛成功。放弃一部分 他们的比赛涉及回溯。除非a。避免回溯 量词已经占用太多,必须放弃它以允许 完全匹配才能成功。

  3. 另一个规则是迭代匹配在该点恢复 上一场比赛没有结束。

  4. 穿过目标字符串,&#34; V&#34;匹配子模式[VI]?(以下称为子模式#2)。贪婪的量词持有&#39; V&#39;并且只有在以后被强制使用时才会释放它,以获得更大的利益。

    &#34; M&#34;从目标字符串匹配子模式#7。并且&#34; V&#34;匹配子模式#9。完成第一次迭代,匹配&#34; VMV&#34;。

    现在,您的目标字符串的其余部分看起来像&#34; ARGDLGVE&#34;。 pos标记位于3(目标字符串中的第4个字符),因此第二次迭代的匹配从那里开始。 &#39; A&#39;子模式#3,&#39; R&#39;比赛#4,&#39; G&#39;比赛#5,&#39; D&#39;比赛在#6,&#39; L&#39;比赛#7,&#39; G&#39;比赛#8,&#39; V&#39;比赛#9,&#39; E&#39;比赛在#10。第二次迭代完成,匹配了#ARGDLGVE&#39;来自目标字符串。

    在第三次迭代中,pos标记位于11,它位于目标字符串中的最后一个字符之后。因此,将空字符串与正则表达式进行比较。因为正则表达式中的每个量词都是&#34; 0或1&#34;,正则表达式可以匹配空字符串。所以第三次迭代完成,匹配&#34;&#34; (空字符串)。

    你有三场比赛:&#34; VMV&#34;,&#34; ARGDLGVE&#34;和&#34;&#34;。

    您可能希望做的一件事是控制pos标记。将正则表达式放在while循环中,在循环终止之前,从字符串的开头向前推进pos个位置。但这只能解决您在上述第三条规则中遇到的问题。你仍然会遇到量词人做非常具体事情的问题,并且只是因为你觉得它很方便而不违反他们自己的规则。

    关键是正则表达式引擎不是置换引擎。它的工作是确定给定的目标字符串是否与给定的模式匹配,遵循一组明确定义的(虽然有时令人困惑)规则。

    我不确定您尝试解决的问题是什么。如果您只是尝试扩展一组范围,那么使用CPAN模块String::Range::Expand可能会获得更好的成功。可能还有其他CPAN模块可以为您进行范围扩展,但这可能是一个很好的起点。

答案 2 :(得分:1)

这与Count overlapping regex matches in Perl OR Ruby几乎相同。

此代码与perldoc perlre几乎没有变化,在标题为“Special Backtracking Control Verbs”的部分中:

use strict;
use warnings;

my $regex = qr/M?[VI]?A?R?G?D?[LM]?G?[IVMAL]?E?/;
my $text  = 'VMVARGDLGVE';

my $count = 0;
$text =~ /$regex(?{print "$&\n"; $count++})(*FAIL)/g;
print "Got $count matches\n";

脚本会计算空字符串匹配,以计算97次匹配。