在Perl正则表达式中匹配平衡的括号

6
我有一个需要拆分并存储到数组中的表达式:
aaa="bbb{ccc}ddd" { aa="bb,cc" { a="b", c="d" } }, aaa="bbb{}" { aa="b}b" }, aaa="bbb,ccc"

一旦被分割并存储在数组中,它应该像这样显示:
aaa="bbb{ccc}ddd" { aa="bb,cc" { a="b", c="d" } }
aaa="bbb{}" { aa="b}b" }
aaa="bbb,ccc"

我使用 Perl 版本 5.8,请问能否解决这个问题?
6个回答

11

使用 Perl 模块 "Regexp::Common"。它有一个不错的平衡括号正则表达式,能够很好地工作。

# ASN.1
use Regexp::Common;
$bp = $RE{balanced}{-parens=>'{}'};
@genes = $l =~ /($bp)/g;

如果没有安装这个 Perl 模块,该如何安装? - pico

10

perlre中有一个例子,使用了v5.10引入的递归正则表达式功能。虽然你只能使用v5.8版本,但其他人来到这个问题应该能得到正确的解决方案 :)

$re = qr{ 
            (                                # paren group 1 (full function)
                foo
                (                            # paren group 2 (parens)
                    \(
                        (                    # paren group 3 (contents of parens)
                            (?:
                                (?> [^()]+ ) # Non-parens without backtracking
                                |
                                (?2)         # Recurse to start of paren group 2
                            )*
                        )
                    \)
                )
            )
    }x;

1
为了匹配平衡的括号或花括号,如果您想考虑反斜杠(转义),那么建议的解决方案将不起作用。相反,您可以编写类似于以下内容的代码(在 perlre 中提供的解决方案基础上):
$re = qr/
(                                                # paren group 1 (full function)
    foo
    (?<paren_group>                              # paren group 2 (parens)
        \(
            (                                    # paren group 3 (contents of parens)
                (?:
                    (?> (?:\\[()]|(?![()]).)+ )  # escaped parens or no parens
                    |
                    (?&paren_group)              # Recurse to named capture group
                )*
            )
        \)
    )
)
/x;

1

我基本上同意Scott Rippey的观点,建议自己编写解析器。以下是一个简单的例子:

my $in = 'aaa="bbb{ccc}ddd" { aa="bb,cc" { a="b", c="d" } }, ' .
         'aaa="bbb{}" { aa="b}b" }, ' .
         'aaa="bbb,ccc"'
;

my @out = ('');

my $nesting = 0;
while($in !~ m/\G$/cg)
{
  if($nesting == 0 && $in =~ m/\G,\s*/cg)
  {
    push @out, '';
    next;
  }
  if($in =~ m/\G(\{+)/cg)
    { $nesting += length $1; }
  elsif($in =~ m/\G(\}+)/cg)
  {
    $nesting -= length $1;
    die if $nesting < 0;
  }
  elsif($in =~ m/\G((?:[^{}"]|"[^"]*")+)/cg)
    { }
  else
    { die; }
  $out[-1] .= $1;
}

(在Perl 5.10中测试过;抱歉,我手头没有Perl 5.8,但据我所知,没有任何相关的差异。)不用说,您肯定想用某些特定于应用程序的内容替换die。您可能还需要调整上述内容以处理未包含在示例中的情况。(例如,引号字符串可以包含\"吗?可以使用'代替"吗?此代码不处理这两种可能性。)

很高兴知道一个Perl专家同意我的答案...我只会说PCRE,所以我的答案做出了大胆的假设,即解析器比可能不可能的正则表达式更容易。 - Scott Rippey
我在这里看不到任何阻止它在Perl5版本8上运行相同的东西。 - Brad Gilbert

0
尝试像这样做:

use strict;
use warnings;
use Data::Dumper;

my $exp=<<END;
aaa="bbb{ccc}ddd" { aa="bb,cc" { a="b", c="d" } }     , aaa="bbb{}" { aa="b}b" }, aaa="bbb,ccc"
END

chomp $exp;
my @arr = map { $_ =~ s/^\s*//; $_ =~ s/\s* $//; "$_}"} split('}\s*,',$exp);
print Dumper(\@arr);

谢谢您的回复。我发现在匹配类似aa="bb},cc"这样的内容时出现了错误。 - meharo

-1

尽管递归正则表达式通常可以用来捕获“平衡的大括号”{},但对于您来说是行不通的,因为您还需要匹配“平衡的引号”"
这对于Perl正则表达式来说是一个非常棘手的任务,我十分确定它是不可能的。(相比之下,使用Microsoft的“平衡组”正则表达式功能可能会做到)。

我建议您创建自己的解析器。当您处理每个字符时,计算每个"{}的数量,并且只在它们“平衡”时进行,的拆分。


1
我认为可以用Perl完成,但对于新手来说并不容易。尤其是使用Regexp::Grammars风格的正则表达式可能更容易些。使用一个真正的解析器会更好,也许可以考虑使用Marpa - Brad Gilbert
Regexp::Grammars 不支持5.8 :( - meharo
这是很有可能的,但我不建议这样做。 :) - brian d foy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接