【问题标题】:Perl all matches of a regexp in a given stringPerl 给定字符串中正则表达式的所有匹配项
【发布时间】:2013-10-01 09:29:54
【问题描述】:

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),可以尝试所有可能的方式来让指定的正则表达式生成给定的字符串?

除其他外,这可以让您实现“POSIX 兼容”正则表达式匹配,其中选择最长的总匹配。就我而言,我真的很想看到每一种可能性。

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

背景

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

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

那么就有一个translate('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';
}

现在 translate('aaab') 返回 'MU'。

【问题讨论】:

  • 您要解决什么问题?这一点都不清楚。您通常无法访问正则表达式引擎尝试匹配的所有不同字符串,而且您的问题听起来根本不像正则表达式的匹配(双关语)。正则表达式匹配返回最后一组匹配项和组。如果您想返回所有可能的匹配项,您将需要一个不同的界面,并且可能性会呈指数级增长。您可能必须破解正则表达式引擎实现才能获得所需的结果。
  • 感谢您的关注。我已编辑问题以显示一个可能的用例。

标签: regex perl posix capture greedy


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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多