匹配Perl中第n个可能的最长字符串

时间:2014-06-28 01:51:06

标签: regex string perl pattern-matching

Perl正则表达式的模式匹配量词是"贪婪" (它们匹配最长的字符串)。迫使比赛成为" ungreedy",a?可以附加到模式量词(*,+)。

以下是一个例子:

#!/usr/bin/perl

$string="111s11111s";

#-- greedy match
$string =~ /^(.*)s/;
print "$1\n"; # prints 111s11111

#-- ungreedy match
$string =~ /^(.*?)s/;
print "$1\n"; # prints 111

但是如何在Perl中找到第二,第三和......可能的字符串匹配?举一个简单的例子 - 如果需要更好的一个。

2 个答案:

答案 0 :(得分:10)

利用conditional expressioncode expressionbacktracking control verbs

my $skips = 1;
$string =~ /^(.*)s(?(?{$skips-- > 0})(*FAIL))/;

以上将使用贪婪匹配,但会导致最大匹配故意失败。如果你想要第三大,你可以将跳过次数设置为2.

以下展示:

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

my $string = "111s11111s11111s";

$string =~ /^(.*)s/;
print "Greedy match     - $1\n";

$string =~ /^(.*?)s/;
print "Ungreedy match   - $1\n";

my $skips = 1;
$string =~ /^(.*)s(?(?{$skips-- > 0})(*FAIL))/;
print "2nd Greedy match - $1\n";

输出:

Greedy match     - 111s11111s11111
Ungreedy match   - 111
2nd Greedy match - 111s11111

使用此类高级功能时,必须充分了解正则表达式以预测结果。这种特殊情况有效,因为正则表达式的一端是^。这意味着我们知道每个后续匹配也比前一个短。但是,如果两端都可能发生变化,我们就无法预测秩序。

如果是这种情况,那么你会发现它们全部,然后你对它们进行排序:

use strict;
use warnings;

my $string = "111s11111s";

my @seqs;
$string =~ /^(.*)s(?{push @seqs, $1})(*FAIL)/;

my @sorted = sort {length $b <=> length $a} @seqs;

use Data::Dump;
dd @sorted;

输出:

("111s11111s11111", "111s11111", 111)

注意v5.18

之前的Perl版本

Perl v5.18引入了一个更改/(?{})/ and /(??{})/ have been heavily reworked,它使词法变量的范围在上面使用的代码表达式中正常工作。在此之前,上述代码会导致以下错误,如this subroutine version run under v5.16.2

中所示
Variable "$skips" will not stay shared at (re_eval 1) line 1.
Variable "@seqs" will not stay shared at (re_eval 2) line 1.

对旧代RE代码表达式实现的修复是使用our声明变量,并且为了进一步的良好编码实践,在初始化时将它们localize。这在modified subroutine version run under v5.16.2中得到了证明,或者如下所示:

local our @seqs;
$string =~ /^(.*)s(?{push @seqs, $1})(*FAIL)/;

答案 1 :(得分:3)

首先获得所有可能的匹配。

my $string = "111s1111s11111s";
local our @matches;
$string =~ /^(.*)s(?{ push @matches, $1 })(?!)/;

找到

111s1111s11111
111s1111
111

然后,它只是找出哪一个是第二个长期并过滤掉其他的。

use List::MoreUtils qw( uniq );

my $target_length = ( sort { $b <=> $a } uniq map length, @matches )[1];

@matches = uniq grep { length($_) == $target_length } @matches
   if $target_length;