11

我有一个 ASCII 日志文件,其中包含一些我想提取的内容。我从来没有花时间正确学习 Perl,但我认为这是完成这项任务的好工具。

该文件的结构如下:

...
...一些垃圾
...
...垃圾开始
我想要的是
在不同的
线条
结尾
...
...更多垃圾...
下一个开始
更多我想要的东西,再次
传播
通过
多行
结尾
...
更多垃圾

所以,我正在寻找一种方法来提取每个字符串STARTEND分隔符字符串之间的行。我怎样才能做到这一点?

到目前为止,我只找到了一些关于如何用START字符串打印一行的示例,或者与我正在寻找的内容有些相关的其他文档项目。

4

6 回答 6

23

您想要触发器运算符(也称为范围运算符)..

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

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

将调用替换为print您实际想要做的任何事情(例如,将行推入数组,编辑它,格式化它,等等)。我正在next超越实际具有STARTor的行END,但您可能不想要这种行为。有关此运算符和其他有用的 Perl 特殊变量的讨论,请参阅本文

于 2009-07-31T14:26:40.637 回答
5

perlfaq6How can I pull outlines between two patterns that are themselves on different lines?


您可以使用 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

在 Telemachus 回复之后,事情开始倾泻而出。毕竟,这可以作为我正在寻找的解决方案。

  1. 我正在尝试在单独的行中提取由两个字符串分隔的行(一个,一行以“CINFILE =”结尾;另一个,一行包含单个“#”),不包括分隔符行。我可以用 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

来自“虚拟新人”也不错。您可以做的一件事是将“$found=1”放在“if($found == 0)”块中,这样您就不会每次在 $start 和 $stop 之间都进行该分配。

在我看来,另一件有点难看的事情是每次输入 $start/$stop-block 时都会打开同一个文件处理程序。

这显示了一种解决方法:

#!/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 回答