【问题标题】:Can I use Perl regular expressions to match balanced text?我可以使用 Perl 正则表达式来匹配平衡的文本吗?
【发布时间】:2013-06-21 23:16:14
【问题描述】:

我想在 Perl 中匹配括号等中的文本。我该怎么做?


这是来自official perlfaq 的问题。我们是importing the perlfaq to Stack Overflow

【问题讨论】:

    标签: regex perl brackets recursive-regex


    【解决方案1】:

    这是official FAQ answer 减去任何后续编辑。

    您的第一次尝试应该是Text::Balanced 模块,它自 Perl 5.8 起就在 Perl 标准库中。它具有多种功能来处理棘手的文本。 Regexp::Common 模块还可以通过提供您可以使用的预设模式来提供帮助。

    从 Perl 5.10 开始,您可以使用递归模式将平衡文本与正则表达式匹配。在 Perl 5.10 之前,您不得不求助于各种技巧,例如在 (??{}) 序列中使用 Perl 代码。

    这是一个使用递归正则表达式的示例。目标是捕获尖括号内的所有文本,包括嵌套尖括号中的文本。此示例文本有两个“主要”组:一个具有一层嵌套的组和一个具有两层嵌套的组。尖括号中总共有五个组:

    I have some <brackets in <nested brackets> > and
    <another group <nested once <nested twice> > >
    and that's it.
    

    匹配平衡文本的正则表达式使用两个新的(Perl 5.10)正则表达式特性。这些在perlre 中有介绍,这个例子是该文档中的一个修改版本。

    首先,将新的所有格+ 添加到任何量词会找到最长的匹配并且不会回溯。这很重要,因为您想通过递归处理任何尖括号,而不是回溯。群组[^&lt;&gt;]++ 找到一个或多个非尖括号,无需回溯。

    其次,新的(?PARNO) 指的是PARNO 给出的特定捕获组中的子模式。在下面的正则表达式中,第一个捕获组找到(并记住)平衡文本,您需要在第一个缓冲区中使用相同的模式才能通过嵌套文本。这就是递归部分。 (?1) 使用外部捕获组中的模式作为正则表达式的独立部分。

    综合起来,你有:

    #!/usr/local/bin/perl5.10.0
    
    my $string =<<"HERE";
    I have some <brackets in <nested brackets> > and
    <another group <nested once <nested twice> > >
    and that's it.
    HERE
    
    my @groups = $string =~ m/
            (                   # start of capture group 1
            <                   # match an opening angle bracket
                (?:
                    [^<>]++     # one or more non angle brackets, non backtracking
                      |
                    (?1)        # found < or >, so recurse to capture group 1
                )*
            >                   # match a closing angle bracket
            )                   # end of capture group 1
            /xg;
    
    $" = "\n\t";
    print "Found:\n\t@groups\n";
    

    输出显示 Perl 找到了两大类:

    Found:
        <brackets in <nested brackets> >
        <another group <nested once <nested twice> > >
    

    通过一些额外的工作,您可以将所有组都放在尖括号中,即使它们也在其他尖括号中。每次获得平衡匹配时,删除其外部分隔符(即您刚刚匹配的分隔符,因此不要再次匹配)并将其添加到要处理的字符串队列中。继续这样做,直到找不到匹配项:

    #!/usr/local/bin/perl5.10.0
    
    my @queue =<<"HERE";
    I have some <brackets in <nested brackets> > and
    <another group <nested once <nested twice> > >
    and that's it.
    HERE
    
    my $regex = qr/
            (                   # start of bracket 1
            <                   # match an opening angle bracket
                (?:
                    [^<>]++     # one or more non angle brackets, non backtracking
                      |
                    (?1)        # recurse to bracket 1
                )*
            >                   # match a closing angle bracket
            )                   # end of bracket 1
            /x;
    
    $" = "\n\t";
    
    while( @queue )
        {
        my $string = shift @queue;
    
        my @groups = $string =~ m/$regex/g;
        print "Found:\n\t@groups\n\n" if @groups;
    
        unshift @queue, map { s/^<//; s/>$//; $_ } @groups;
        }
    

    输出显示所有组。最外层的匹配首先显示,嵌套的匹配稍后显示:

    Found:
        <brackets in <nested brackets> >
        <another group <nested once <nested twice> > >
    
    Found:
        <nested brackets>
    
    Found:
        <nested once <nested twice> >
    
    Found:
        <nested twice>
    

    【讨论】:

      猜你喜欢
      • 2011-12-19
      • 1970-01-01
      • 1970-01-01
      • 2013-06-30
      • 2010-10-07
      相关资源
      最近更新 更多