2017-11-17 9 views
0

と複数行のデータの文字列を解析:私のようなデータ解析しようとしています正の後読み

header1 
------- 
var1 0 
var2 5 
var3 9 
var6 1 

header2 
------- 
var1 -3 
var3 5 
var5 0 

を今、私は例えば取得したいですヘッダー2の場合はvar3。これを行うにはどうすればよいでしょうか?

これまでのところ、私は

open(FILE,"< $file"); 
while (my $line = <FILE>){ 
    # do stuff 
} 

を経由してライン・バイ・ラインを自分のファイルを解析しましたが、私はそれが適切に複数行の構文解析を処理することはできませんだと思います。

今、私は一度にファイルを解析するために考えていたが成功しなかったのです、これまで...

my @Input; 
open(FILE,"< $file"); 
while (<FILE>){ @Input = <FILE>; } 
if (@Input =~ /header2/){ 
    #... 
} 
+1

「*でも、マルチライン解析を適切に処理することはできないと思います。」 - 実際のコードをさらに表示すると、その問題は解決できる可能性がありますか?おそらく、複雑な正規表現を使わなくても簡単な解決法があります。 "*今はすぐにファイルを解析することを考えていますが、これまで成功していませんでした... *" - これがどのように機能しないのかを明確にすることはできますか?そのスニペットにはいくつかの問題がありますが、それらはすべてあなたのタイトル(lookbehindを持つregexes)の問題とは無関係です。貴重な情報はhttps://perlmaven.com/slurpを参照してください。 – amon

答えて

2
while (<FILE>){ @Input = <FILE>; } 

これはあまり意味がありません。 「FILEからレコードを読み取ることができますが、FILEのすべてのデータを@Inputに読み込みます。

my @Input = <FILE>; 
if (@Input =~ /header2/){ 

これはあまりにも非常に奇妙です:私はあなたが実際にしたいことだけだと思います。バインディング演算子(=~)は、スカラーオペランドを必要とするため、スカラーコンテキストの両方のオペランドを評価します。つまり、@Inputは、@Inputの要素の数として評価されます。これは整数で、 "header2"とは決して一致しません。

いくつかのアプローチがあります。まず、正規表現のアプローチ。

#!/usr/bin/perl 

use strict; 
use warnings; 
use feature 'say'; 

my $file = 'file'; 

open my $fh, '<', $file or die $!; 

my $data = join '', <$fh>; 

if ($data =~ /header2.+var3 (.+?)\n/s) { 
    say $1; 
} else { 
    say 'Not found'; 
} 

これの鍵は、m//オペレーターに/sです。それがなければ、正規表現の2つの点は改行にマッチしません。

もう1つのアプローチは、行単位のパーサーのほうが多くなります。

#!/usr/bin/perl 

use strict; 
use warnings; 
use feature 'say'; 

my $file = 'file'; 

open my $fh, '<', $file or die $!; 

my $section = ''; 

while (<$fh>) { 
    chomp; 
    # if the line all word characters, 
    # then we've got a section header. 
    if ($_ !~ /\W/) { 
    $section = $_; 
    next; 
    } 

    my ($key, $val) = split; 
    if ($section eq 'header2' and $key eq 'var3') { 
    say $val; 
    last; 
    } 
} 

ファイルを一度に1行ずつ読み込み、セクションヘッダーを書き留めます。データラインについては、空白を分割し、正しいセクションにあるかどうかを確認し、正しいキーを持っているかどうかを確認します。

どちらの場合も、私はファイルを開くためのより標準的なアプローチ(字句ファイルハンドル、3-arg open()or die $!)を使用するように切り替えました。

+0

私は以前に '/ s'で正規表現のアプローチを試みましたが、' .'が2つのキーワードの間でできるだけ一致すると考えました。 header1 - var2を検索すると、header2 - var2と再び一致しますか?それともこの場合ではないのですか? – EverythingRightPlace

+0

あなたはそうです。 'header2'はあなたのファイルの最後のヘッダーなので、うまく動作しません。私の2番目の解決策はまだ動作します。 –

+0

'header1(?:。+?)var2'では魅力的に機能します。 – EverythingRightPlace

3

これを処理する簡単な方法は「段落モード」です。

local $/ = ""; 
while (<>) { 
    my ($header, $body) =~ /^([^\n]*)\n-+\n(.*)/s 
     or die("Bad data"); 

    my @data = map [ split ], split /\n/, $body; 

    # ... Do something with $header and @data ... 
} 

同じことが以下のように$/いじりなしに達成することができる。

my @buf; 
while (1) { 
    my $line = <>; 
    $line =~ s/\s+\z// if !defined($line); 
    if (!length($line)) { 
     if (@buf) { 
      my $header = shift(@buf); 
      shift(@buf); 
      my @data = map [ split ], splice(@buf); 

      # ... Do something with $header and @data ... 
     } 

     last if !defined($line); 
     next; 
    } 

    push @buf, $line; 
} 

(実際には、第二のスニペットは、第1の過小さな改善のカップルを含みます。)あなたの試みで

クイックコメント:@Input = <FILE>@Inputにファイルの残りの行を配置するので

  • whileループは無用です。
  • @Input =~ /header2/は、@Inputの要素数のストリング化である配列のストリング化に対してheader2と一致します。 @Inputの要素にheader2が含まれているかどうかを確認する場合は、@Inputsの要素をループして個別にチェックする必要があります。
関連する問題