如何从Perl正则表达式字符串中的任意嵌套子组中删除捕获?我想将任何正则表达式嵌入到一个包络表达式中,该表达式将子正则表达式捕获为整个实体以及静态已知的后续组.我是否需要手动将正则表达式字符串转换为使用所有非捕获(?:)组(并希望我不会搞砸),或者是否有提供此功能的Perl正则表达式或库机制?
# How do I 'flatten' $regex to protect $2 and $3?
# Searching 'ABCfooDE' for 'foo' OK, but '((B|(C))fo(o)?(?:D|d)?)', etc., breaks.
# I.E., how would I turn it effectively into '(?:(?:B|(?:C))fo(?:o)?(?:D|d)?)'?
sub check {
my($line, $regex) = @_;
if ($line =~ /(^.*)($regex)(.*$)/) {
print "<", $1, "><", $2, "><", $3, ">\n";
}
}
Run Code Online (Sandbox Code Playgroud)
附录:我隐约知道的$&,$`以及$'与已被告知,如果可能避免他们,我没有访问${^PREMATCH},${^MATCH}并${^POSTMATCH}在我的Perl 5.8环境.上面的例子可以使用这些方法划分为2/3块,更复杂的实例可以手动迭代这个,但我想如果可能的话我想要一个通用的解决方案.
接受的答案:我希望存在并且令人惊讶(至少对我来说)不是,是一个封装组,使其内容不透明,以便随后的位置反向引用将内容视为单个实体,并且名称引用被取消范围. 对于Perl 5.10+,gbacon有一个潜在有用的解决方法,FM显示了在特定情况下可以实现相同效果的任何版本的手动迭代机制,但j_random_hacker称它没有真正的语言机制来封装子表达式.
一般来说,你不能.
即使您可以将所有(...)s转换为(?:...)s,这在一般情况下也不起作用,因为该模式可能需要反向引用:例如/(.)X\1/,匹配任何字符,后跟a X,后跟最初匹配的字符.
因此,如果没有Perl机制来"在事后"丢弃捕获的结果,则无法解决所有正则表达式的问题.你可以做的最好(或者如果你有Perl 5.10就可以做)是使用gbacon的建议,并希望为捕获缓冲区生成一个唯一的名称.
保护您关心的子模式的一种方法是使用命名捕获缓冲区:
此外,从Perl 5.10.0开始,您可以使用命名捕获缓冲区和命名反向引用.符号是
(?<name>...)声明和\k<name>引用.您也可以使用撇号而不是尖括号来分隔名称; 并且您可以使用括号中的\g{name}反向引用语法.也可以通过绝对和相对数量来引用命名的捕获缓冲区.在模式之外,通过%+散列可以获得命名的捕获缓冲区.当相同的图案内的不同的缓冲液具有相同的名称,$+{name}并且\k<name>指的是最左边的定义的组.
在你的问题的背景下,check成为
sub check {
use 5.10.0;
my($line, $regex) = @_;
if ($line =~ /(^.*)($regex)(.*$)/) {
print "<", $+{one}, "><", $+{two}, "><", $+{three}, ">\n";
}
}
Run Code Online (Sandbox Code Playgroud)
然后用它来调用它
my $pat = qr/(?<one>(?<two>B|(?<three>C))fo(o)?(?:D|d)?)/;
check "ABCfooDE", $pat;
Run Code Online (Sandbox Code Playgroud)
输出
<CfooD><C><C>
这不能解决一般情况,但您的具体示例可以使用/g标量上下文中的选项进行处理,这样您就可以将问题分成两个匹配,第二个匹配第一个左边的匹配:
sub check {
my($line, $regex) = @_;
my ($left_side, $regex_match) = ($1, $2) if $line =~ /(^.*)($regex)/g;
my $right_side = $1 if $line =~ /(.*$)/g;
print "<$left_side> <$regex_match> <$right_side>\n"; # <AB> <CfooD> <E123>
}
check( 'ABCfooDE123', qr/((B|(C))fo(o)?(?:D|d)?)/ );
Run Code Online (Sandbox Code Playgroud)