11

抽出したいコンテンツを含む ASCII ログ ファイルがあります。私は Perl をきちんと学ぶために時間をかけたことはありませんが、これはこの作業に適したツールだと思います。

ファイルは次のように構成されています。

...
...いくつかのゴミ
...
~ごみSTART
私が欲しいのは
別の
行
終わり
...
...さらにゴミ...
次のスタート
もっと欲しいもの、また
展開する
終えた
複数行
終わり
...
より多くのゴ​​ミ

STARTそこで、それぞれの区切り文字列と区切り文字列の間の行を抽出する方法を探していENDます。これどうやってするの?

これまでのところ、START文字列を含む行を印刷する方法の例や、探しているものに多少関連するその他のドキュメント項目を見つけただけです。

4

6 に答える 6

23

フリップフロップ演算子 (範囲演算子とも呼ばれる) が必要な場合..

#!/usr/bin/env perl
use strict;
use warnings;

while (<>) {
  if (/START/../END/) {
    next if /START/ || /END/;
    print;
  }
}

printへの呼び出しを、実際にやりたいこと (たとえば、行を配列にプッシュする、編集する、フォーマットするなど) に置き換えます。実際にorが含まnextれる行を通り過ぎていますが、その動作が望ましくない場合があります。この演算子とその他の便利な Perl 特殊変数の説明については、この記事を参照してください。STARTEND

于 2009-07-31T14:26:40.637 に答える
5

perlfaq6の回答から、それ自体が異なる行にある 2 つのパターン間の行をどのように引き出すことができますか?


Perl のやや風変わりな .. 演算子を使用できます (perlop に文書化されています):

perl -ne 'print if /START/ .. /END/' file1 file2 ...

行ではなくテキストが必要な場合は、次を使用します

perl -0777 -ne 'print "$1\n" while /START(.*?)END/gs' file1 file2 ...

しかし、START から END までのネストされたオカレンスが必要な場合は、バランスの取れたテキストの一致に関するこのセクションの質問で説明されている問題に直面することになります。

.. を使用する別の例を次に示します。

while (<>) {
    $in_header =   1  .. /^$/;
    $in_body   = /^$/ .. eof;
# now choose between them
} continue {
    $. = 0 if eof;  # fix $.
}
于 2009-07-31T18:03:50.117 に答える
1

Perlで一致する行の後に複数の行を取得するにはどうすればよいですか?

あれはどう?その中で、END 文字列は $^ です。これを END 文字列に変更できます。

私も初心者ですが、そこにあるソリューションはかなりの数の方法を提供しています...上記のリンクとは異なるものをより具体的に教えてください。

于 2009-07-31T14:26:37.640 に答える
1
while (<>) {
    chomp;      # strip record separator
    if(/END/) { $f=0;}
    if (/START/) {
        s/.*START//g;
        $f=1;
    }
    print $_ ."\n" if $f;
}

次回はコードを書いてみる

于 2009-07-31T14:29:41.893 に答える
1

テレマコスの返事の後、事態は急展開を始めた。これは、結局のところ、私が見ているソリューションとして機能します。

  1. 区切り行を除いて、2 つの文字列 (1 つは "CINFILE=" で終わる行、もう 1 つは単一の "#" を含む行) で区切られた行を別々の行に抽出しようとしています。これは Telemachus のソリューションで実現できます。
  2. 最初の行には、削除したいスペースがあります。私も含めています。
  3. また、各行セットを個別のファイルに抽出しようとしています。

コードは醜いものとして分類できますが、これは私にとってはうまくいきます。これは、私が現在 Perl にほぼ慣れていないためです。とにかくここに行きます:

#!/usr/bin/env perl
use strict;
use warnings;

my $start='CINFILE=$';
my $stop='^#$';
my $filename;
my $output;
my $counter=1;
my $found=0;

while (<>) {
  if (/$start/../$stop/) {
    $filename=sprintf("boletim_%06d.log",$counter);
    open($output,'>>'.$filename) or die $!;
    next if /$start/ || /$stop/;
    if($found == 0) { print $output (split(/ /))[1]; }
    else { print $output $_; }
    $found=1;
  } else { if($found == 1) { close($output); $counter++; $found=0; } }
}

それが他の人にも利益をもたらすことを願っています。乾杯。

于 2009-07-31T15:44:02.270 に答える
1

「仮想新人」から来るのも悪くない。できることの 1 つは、「if($found == 0)」ブロック内に「$found=1」を配置して、$start と $stop の間で毎回その割り当てを行わないようにすることです。

私の意見では、少し醜いもう 1 つの点は、$start/$stop ブロックに入るたびに同じファイルハンドラーを開くことです。

これはそれを回避する方法を示しています:

#!/usr/bin/perl

use strict;
use warnings;

my $start='CINFILE=$';
my $stop='^#$';
my $filename;
my $output;
my $counter=1;
my $found=0;

while (<>) {

    # Find block of lines to extract                                                           
    if( /$start/../$stop/ ) {

        # Start of block                                                                       
        if( /$start/ ) {
            $filename=sprintf("boletim_%06d.log",$counter);
            open($output,'>>'.$filename) or die $!;
        }
        # End of block                                                                         
        elsif ( /$end/ ) {
            close($output);
            $counter++;
            $found = 0;
        }
        # Middle of block                                                                      
        else{
            if($found == 0) {
                print $output (split(/ /))[1];
                $found=1;
            }
            else {
                print $output $_;
            }
        }

    }
    # Find block of lines to extract                                                           

}
于 2009-08-05T10:29:36.167 に答える