2016-05-04 9 views
3

()括弧で囲まれたテキストファイルから文章を印刷したいと思います。このテキストファイルの例1つの括弧 '()'よりも深いネストされた文を検索するにはどうすればよいですか?

blabla(nothing(print me)) nanana (nanan) 
blablabla(aaaaaaa(eeee(bbbb(cccc)bbb))aa) 
blabla (blabla(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";} 

私のコードができます。

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

これは私がこれまで何をやったかであります深い括弧で囲まれた文を区切ります。

+1

を印刷し、これはUbuntuのとは何の関係もありません。 Perlの正規表現は、すべてのオペレーティングシステムで一貫しています(少なくともPerlの特定バージョンの場合)。 –

+0

私は使用しているシステムの情報のタイトルにUbuntuと入力しました。間違って申し訳ありません:) – Piodo

+2

@Piodo謝罪する必要はありません。通常、十分でない情報よりも多くの情報を与える方が良いです。 – ThisSuitIsBlackNot

答えて

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 

EDIT:このパターンは括弧がバランスしていることを前提としていますが、それが当てはまらない場合は、これは特定の文字列のため、不要な結果の問題を引き起こす可能性があります。その理由は、パターン全体が成功する前に結果が保存されるからです。閉じ括弧が欠落している文字列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 
    

    は、問題を解決するには、次の2つのデフォルトの動作を選択することができます。一時的な配列に結果を格納し、whileループまたは閉じ括弧がない場合にこの配列をリセットするだけです。この場合、結果は唯一"efgh"次のようになります。

my @a; 
my @b; 

while (<DATA>) { 
    while (/\(([^()]*(?:\(((?1))\)[^()]*(?{push @b, $2}))*+)(?:\)|(?{undef @b})(*F))/g) { 
     push @a, @b; 
     undef @b; 
    } 
} 
  • 必須閉じ括弧をしないより寛容行動。これを行うには、それぞれ\)(?:\)|$)に置き換える必要があります。この場合、最初の試行は成功し、文字列の最後まで文字を消費します(つまり、2回目の試行はありません)。結果は"efgh""abcd(efgh)ijkl"
+1

@ThisSuitIsBlackNot:実際には、 '?{}'構文を使う方法や、 '\ G'と先読みを使ってパターンを構築してネストした結果を得る方法を実際に探しています。 –

+1

いい仕事です!これはとてもうまくいっています。コードは本当に短いです、私はそれに感銘を受けます。しかし私は深刻なバグではないことを発見しました。入力に大きなレベルの括弧がある場合、コードは結果をダブリングすることがあります。たとえば、入力: 'nanana((print 2)(print 3(print 5)))') 'にはprint2が結果に2倍になっています。それは深刻な問題ではありません。私はこの形式であなたのコードで大丈夫です:) – Piodo

+0

@Piodo:私は問題を発見し、できるだけ早く編集を投稿します。問題は、完全なパターンが成功する前に結果が保存されるため、括弧がバランスしていない例では、最初の開きブラケット位置で失敗した結果が格納され、この位置は失敗します(閉じ括弧がありません)。第3の開口部ブラケットでの他の試みも結果をもたらす。それが重複したアイテムを取得する理由です。 –

2

です。これはおそらく最も簡単で、2パスの解決策で最も保守的です。

最初のパスはすべての第1レベルのカッコを取得します。第二のパスは、埋め込まれた括弧グループのすべてのレベルに一致させるために単一の文字を進め、すべての囲まれた括弧群をキャプチャ:

#!/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 
+0

'----(--a(aaaa(123)bbbb(456)cccc)----)----'とすると、連続したネストされた括弧で動作するのを止めます。 '456'。 *(前にこのような方法を試しました)* –

+1

@CasimiretHippolyteうん、そうです。ほとんどが彼が提供したデータを使って概念証明することになりました。しかし、私は先に進んで余分なユースケースをカバーするようにアプローチを調整しました。 – Miller

+0

私はあなたの足にキスをすることがわかったら。 –

2

このコードは使用して、再帰的レベルを捕捉することによって機能します簡単な正規表現)split-(の開封記号です。それは最初にネストの2つの開始レイヤーをはがすことによって準備します。これは、示された例やその他いくつかの例で機能します。ただし、ルールが指定されていないペアをネストする他の方法もあります。また、これはおそらくエッジの周りに荒いです。どんな種類の魔法もなく、新しいケースの調整コードも実行可能でなければなりません。

use warnings; 
use strict; 

my ($lev, @el, @res, $rret); 
while (my $str = <DATA>) 
{ 
    print "\nString: $str\n"; 
    @res =(); 

    # Drop two layers to start: strip last two), split by (and drop 0,1 
    $str =~ s/ (.*) \) [^)]* \) [^)]* $/$1/x; 
    @el = split '\(', $str; 
    @el = @el[2..$#el]; 
    # Edge case: may have one element and be done, but with extra) 
    if (@el > 1) { $lev = join '(', @el } 
    else   { ($lev = $el[0]) =~ s|\)||g } 
    push @res, $lev; 

    # Get next level and join string back, recursively 
    while ($rret = nest_one($lev)) { 
     $lev = join '(', @$rret; 
     push @res, $lev; 
     last if @$rret == 1; 
    } 

    print "\t$_\n" for @res; 
} 

# Strip last) and past it, split by (and drop first element 
sub nest_one { 
    (my $lev = $_[0]) =~ s/(.*) \) [^)]* $/$1/x; 
    my @el = split '\(', $lev; 
    shift @el; 
    return (@el) ? \@el : undef; 
} 

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

それはFYI

 
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 
関連する問題