如何找到嵌套得比一个括号'()'集更深的句子?

时间:2016-05-04 22:09:46

标签: regex perl

我想打印放在()括号中的文本文件中的句子,而不是一对括号。

例如,对于此文本文件:

blabla(nothing(print me)) nanana (nanan)
blablabla(aaaaaaa(eeee(bbbb(cccc)bbb))aa)
blabla (blabla(hhhhh))

输出应为:

print me
eeee(bbbb(cccc)bbb)
bbbb(cccc)bbb
cccc
hhhhh

这是我到目前为止所做的:

#!/usr/bin/perl -w

open(FILE, "<", $ARGV[0]) or die "file open error";

if ( @ARGV ) #if there are args
{
    if ( -f $ARGV[0] ) #if its regular file
    {
      while(<FILE>)
      {
        my @array =  split('\)',$_);
        foreach(@array)
        {
          if ($_ =~ /.*\((.*)/) 
          {
            print "$1\n";
          }
        }
      }
    close(FILE);
}
else{
print "Arg is not a file\n";}
}
else{
print "no args\n";}

我的代码无法将更深层括号中的句子分开。

3 个答案:

答案 0 :(得分:2)

假设括号是平衡的:

use strict;
use warnings;

my @a;

while (<DATA>) {
    while (/\(([^()]*(?:\(((?1))\)[^()]*(?{push @a, $2}))*+)\)/g){}
}

print join "\n", @a;

__DATA__
blabla(nothing(print me)) nanana (nanan)
blablabla(aaaaaaa(eeee(bbbb(cccc)bb(xxxx)b))aa)
blabla (blabla(hhhhh))

它返回:

print me
cccc
xxxx
bbbb(cccc)bb(xxxx)b
eeee(bbbb(cccc)bb(xxxx)b)
hhhhh

我们的想法是在每次递归后存储捕获组2内容,使用(?{...})构造在模式中执行代码。

请注意,结果的顺序并不理想,因为最里面的内容首先出现。不幸的是,我没有找到改变结果顺序的方法。

模式细节:

\(  # opening bracket level 1
(   # open capture group 1
    [^()]*        # all that is not a bracket
    (?:
        \(        # opening bracket for level 2 (or more when a recursion occurs)
        (         # capture group 2: to store the result
            (?1)  # recursion
        )
        \)        # closing bracket for level 2 (or more ...)
        [^()]*    # 
        (?{push @a, $2}) # store the capture group 2 content in @a
    )*+ # repeat when needed
)
\) # closing bracket level 1

编辑:此模式假定括号是平衡的,但如果不是这种情况,则可能会导致某些字符串出现意外结果的问题。原因是结果在整个模式成功之前存储。

字符串1234 ( 5678 (abcd(efgh)ijkl)的示例,其中缺少右括号:

1234 ( 5678 (abcd(efgh)ijkl)
#    ^      ^---- second attempt succeeds, "efgh" is stored
#    '---- first attempt fails, but "efgh", "abcd(efgh)ijkl" are stored

要解决此问题,您可以选择两种默认行为:

  • 只接受平衡括号的严格行为。您只需将结果存储在临时数组中,并在while循环中或缺少结束括号时重置此数组。在这种情况下,结果只会是"efgh"
my @a;
my @b;

while (<DATA>) {
    while (/\(([^()]*(?:\(((?1))\)[^()]*(?{push @b, $2}))*+)(?:\)|(?{undef @b})(*F))/g) {
        push @a, @b;
        undef @b;
    }
}
  • 一种更宽容的行为,并不强制要求结束。为此,您必须将每个\)替换为(?:\)|$)。在这种情况下,第一次尝试成功并消耗字符直到字符串结束(换句话说,没有第二次尝试)。结果为"efgh""abcd(efgh)ijkl"

答案 1 :(得分:2)

这可能是最简单的,并且通过双程解决方案可以维护得最多。

初始传递捕获所有第一级括号。第二遍捕获所有括号括起的组,只推进单个字符以匹配每个级别的嵌入式paren组:

#!/usr/bin/env perl

use strict;
use warnings;
use v5.10;

my $data = do { local $/; <DATA> };

my $parens_content_re = qr{
    \(
        (
            (?:
                [^()]*+
                |
                \( (?1) \)
            )*
        )
    \)
}x;

say for map {/(?=$parens_content_re)\(/g} map {/$parens_content_re/g} $data;

__DATA__
blabla(nothing(print me)) nanana (nanan)
blablabla(aaaaaaa(eeee(bbbb(cccc)bbb))aa)
blabla (blabla(hhhhh))
----(----(aaaa(123)bbbb(456)cccc)----)----

输出:

$ perl parens.pl
print me
eeee(bbbb(cccc)bbb)
bbbb(cccc)bbb
cccc
hhhhh
aaaa(123)bbbb(456)cccc
123
456

答案 2 :(得分:2)

此代码的工作原理是递归捕获级别,使用)的简单正则表达式和split的{​​{1}}作为开始表达式。它首先通过剥离两个起始层的嵌套来准备。它适用于显示的示例和其他一些示例。但是,还有其他方法可以嵌套对,但未指定规则。此外,这可能是边缘粗糙。没有涉及任何类型的魔法,调整新案例的代码应该是可行的。

(

打印

blabla(nothing(print me)) nanana (nanan)

        print me

blablabla(aaaaaaa(eeee(bbbb(cccc)bbb))aa)

        eeee(bbbb(cccc)bbb)
        bbbb(cccc)bbb
        cccc

blabla (blabla(hhhhh))

        hhhhh