【发布时间】: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