繁体   English   中英

perl使用触发器在SAME分隔符之间提取文本

[英]perl extract text between SAME delimiter using flip-flop

在过去我具有不同的START&END的地方,我已经能够使用触发器提取文本。 这次我在尝试提取文本时遇到了很多麻烦,因为我的源文件中没有不同的分隔符,因为触发器的START和END相同。 我希望当行的年份为yyyy时触发器开始为真,并继续将$_推入数组,直到另一行开始yyyy。 触发器的问题在于,在我的下一次启动时,触发器将为假。

while (<SOURCEFILE>) {
  print if (/^2017/ ... /^2017/) 
}

将上述内容用于给定的源数据将错过我也需要匹配的文件的第二个多行部分。 也许我认为这是解析多行文件的最佳方法的触发器在这种情况下不起作用? 我要做的是从日期开始的第一行开始匹配,并继续匹配直到以日期开始的下一行之前的行。

样本数据为:

2017 message 1
Text
Text

Text

2017 message 2
more text
more text

more text

2017 message 3
yet more text
yet more text

yet more text

但我得到:

2017 message 1
Text
Text

Text

2017 message 2
2017 message 3
yet more text
yet more text

yet more text

...缺少消息2的内容。

我不能在源数据中依赖空格或其他END分隔符。 我想要的是打印每条消息(实际上是push @myarray, $_ ,然后测试是否匹配),但是在这里我缺少消息2下方的行,因为触发器设置为false。 有什么办法可以通过触发器处理,还是我需要使用其他东西? 在此先感谢您可以提供帮助/建议的任何人。

这是一种方法:

use Modern::Perl;
use Data::Dumper;
my $part = -1;
my $parts;
while(<DATA>) {
    chomp;
    if (/^2017/ .. 1==0) {
        $part++ if /^2017/;
        push @{$parts->[$part]}, $_;
    }
}
say Dumper$parts;

__DATA__
2017 message 1
Text
Text

Text

2017 message 2
more text
more text

more text

2017 message 3
yet more text
yet more text

yet more text

输出:

$VAR1 = [
          [
            '2017 message 1',
            'Text',
            'Text',
            '',
            'Text',
            ''
          ],
          [
            '2017 message 2',
            'more text',
            'more text',
            '',
            'more text',
            ''
          ],
          [
            '2017 message 3',
            'yet more text',
            'yet more text',
            '',
            'yet more text'
          ]
        ];

我不知道如何使用触发器。 我一年前尝试过。 但是,我在逻辑上做了同样的事情。

my $line_concat;
my $f = 0;
while (<DATA>) {
    if(/^2017/ && !$f) {
        $f = 1;
    }

    if (/^2017/) {
        print "$line_concat\n" if $line_concat ne "";
        $line_concat = "";
    }

    $line_concat .= $_ if $f;
}

print $line_concat if $line_concat ne "";

正如您所发现的那样,带有匹配定界符的触发器不太好用。

您是否考虑过设置$/

例如:

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

local $/ = "2017 message";
my $count;

while ( <DATA> ) {

    print "\nStart of block:", ++$count, "\n";

    print;

    print "\nEnd of block:", $count, "\n";
}

__DATA__
2017 message 1
Text
Text

Text

2017 message 2
more text
more text

more text

2017 message 3
yet more text
yet more text

yet more text

尽管它并不完美,但是因为它会在定界符上分割文件-这意味着第一个文件之前有一个“位”(因此您得到4个块)。 您可以通过明智地使用'chomp'重新拼接它,从而从当前块中删除$/

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

local $/ = "2017 message";
my $count;

while ( <DATA> ) {
    #remove '2017 message'
    chomp;
    #check for empty (first) block
    next unless /\S/;
    print "\nStart of block:", ++$count, "\n";
    #re add '2017 message'
    print $/;
    print;

    print "\nEnd of block:", $count, "\n";
}

或者,关于数组数组,每次您敲击消息时更新“目标键”又如何呢?

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

use Data::Dumper;

my %messages; 
my $message_id;
while ( <DATA> ) {
   chomp;
   if ( m/2017 message (\d+)/ ) { $message_id = $1 }; 
   push @{ $messages{$message_id} }, $_; 
}

print Dumper \%messages;

注意-我使用的是散列,而不是数组,因为这对于不从零开始连续进行的消息排序更加健壮。 (使用这种方法的数组将具有一个空的“ zeroth”元素)。

注意-空行也将带有'empty' ''元素。 您可以根据需要过滤这些内容。

您只需要一个缓冲区来累积行,直到找到一个匹配的/^20\\d\\d[ ]/或文件末尾。

my $in = 0;
my @buf;
while (<>) {
   if ($in && /^20\d\d[ ]/) {
      process(@buf);
      @buf = ();
      $in = 0;
   }

   push @buf, $_ if $in ||= /^2017[ ]/;
}

process(@buf) if $in;

我们可以重新整理代码,使其因此记录在一个地方时才会处理,使process被内联。

my $in = 0;
my @buf;
while (1) {
   $_ = <>;

   if ($in && (!defined($_) || /^20\d\d[ ]/)) {
      process(@buf);
      @buf = ();
      $in = 0;
   }

   last if !defined($_);

   push @buf, $_ if $in ||= /^2017[ ]/;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM