[英]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.