Perl给定字符串中的正则表达式的所有匹配项

时间:2013-09-25 16:53:01

标签: regex perl posix capture greedy

Perl的正则表达式匹配是贪婪的,所以正则表达式

/\A (a+) (.+) \z/x

匹配字符串'aaab',将设置$ 1 ='aaa'和$ 2 ='b'。 (\ A和\ z只是强制字符串的开头和结尾。)

您还可以提供非贪婪的限定符,如

/\A (a+?) (.+?) \z/x

这仍然匹配,但是给$ 1 ='a'和$ 2 ='aab'。

但我想检查所有可能的方法来生成字符串,这是

$1='aaa' $2='b'
$1='aa'  $2='ab'
$1='a'   $2='aab'

第一种方式对应于默认的左贪婪行为,第三种方式对应于使第一次匹配非贪婪,但可能存在这些极端之间的方式。是否有一个正则表达式引擎(无论是Perl,还是其他一些如PCRE或RE2)可以尝试所有可能的方法,指定regexp生成给定的字符串?

除此之外,这将允许您实现“POSIX兼容”正则表达式匹配,其中选择最长的总匹配。在我的情况下,我真的希望看到所有可能性。

(一种方法是使用正则表达式本身,在第一次尝试时用+ 1,1}替换+修饰符,然后用{1,2},{1,3}依旧替换 - 对于每个+的组合正则表达式中的*修饰符。这非常费力且缓慢,并且停止时并不明显。我希望能有更聪明的东西。)

背景

要回答Jim G.关于这可能解决的问题的问题,请考虑规则给出的两种语言之间基于规则的翻译系统

translate(any string of one or more 'a' . y) = 'M' . translate(y)
translate('ab') = 'U'

然后有翻译('aaab')的可能结果,即'MU'。 您可以尝试将这些规则放在基于正则表达式的Perl代码中,如

our @m;
my @rules = (
  [ qr/\A (a+) (.*) \z/x => sub { 'M' . translate($m[1]) } ],
  [ qr/\A ab        \z/x => sub { 'U'                    } ],
);

其中translate遍历每个@rules并尝试依次应用它们:

sub translate {
    my $in = shift;
    foreach (@rules) {
        my ($lhs, $rhs) = @$_;
        $in =~ $lhs or next;
        local @m = ($1, $2);
        my $r = &$rhs;
        next if index($r, 'fail') != -1;
        return $r;
    }
    return 'fail';
}

但是,调用translate('aaab')会返回'fail'。这是因为 它尝试应用匹配(a +)(。*)和正则表达式的第一个规则 引擎找到与最长字符串'a'的匹配。

使用ikegami建议的答案,我们可以尝试各种方式 正则表达式生成字符串:

use re 'eval';
sub translate {
    my $in = shift;
    foreach (@rules) {
        my ($lhs, $rhs) = @$_;
        local our @matches;
        $in =~ /$lhs (?{ push @matches, [ $1, $2 ] }) (*FAIL)/x;
        foreach (@matches) {
            local @m = @$_;
            my $r = &$rhs;
            next if index($r, 'fail') != -1;
            return $r;
        }
    }
    return 'fail';
}

现在翻译('aaab')返回'MU'。

1 个答案:

答案 0 :(得分:4)

local our @matches;
'aaab' =~ /^ (a+) (.+) \z (?{ push @matches, [ $1, $2 ] }) (*FAIL)/x;