Re: Printing line before matching expression
by kcott (Archbishop) on Sep 10, 2013 at 23:50 UTC
|
G'day rm,
Welcome to the monastery.
You just need to keep a sufficient history of previous lines so that you go back to the previous nth line.
Here's one way to do it:
$ perl -Mstrict -Mwarnings -Mautodie -le '
my $file = "pm_1053380_data.txt";
my ($match, $before) = @ARGV;
my @previous;
open my $fh, "<", $file;
while (<$fh>) {
chomp;
if ($_ eq $match) {
print $previous[0];
}
push @previous, $_;
shift @previous if @previous > $before;
}
' line4 2
line2
The input file I used:
$ cat pm_1053380_data.txt
line1
line2
line3
line4
line5
Notes:
-
You'll need to add some validation to check that you can actually look back the number of specified lines.
-
I see you had chop and then commented it out.
You probably want chomp (as I used).
| [reply] [Watch: Dir/Any] [d/l] [select] |
Re: Printing line before matching expression
by johngg (Canon) on Sep 10, 2013 at 23:52 UTC
|
You have to keep a buffer of previously read lines large enough to retain the nth previous line you are wanting to print. How to handle the situation where there aren't n lines before your found line is an exercise left to the reader.
$ perl -Mstrict -Mwarnings -E '
open my $inFH, q{<}, \ <<EOD or die $!;
line 1
line 2
line 3
line 4
line 5
line 6
line 7
line 8
line 9
EOD
my $lineBeforeMatch = 5;
my @prevLines;
my $lookFor = qr{line 7};
while ( <$inFH> )
{
push @prevLines, $_;
shift @prevLines if $#prevLines > $lineBeforeMatch;
print $prevLines[ 0 ] if m{$lookFor};
}'
line 2
$
I hope this is helpful.
| [reply] [Watch: Dir/Any] [d/l] |
Re: Printing line before matching expression
by jaredor (Priest) on Sep 11, 2013 at 05:35 UTC
|
For fun, here's a version that uses a linked list. The "defined" ensures that there is something to be printed. If you change $lookback to 4 and rerun, you'll see it in action.
#!/usr/bin/env perl
use strict;
use warnings;
my $lookback = 3;
my $match = qr/\wiz/;
my @lbuff = map { [] } 1..$lookback;
$lbuff[$_-1][0] = $lbuff[($_) % $lookback] for 1..$lookback;
my $curr = $lbuff[0];
while (<DATA>) {
/$match/ and defined $$curr[1] and print "$$curr[1]";
($$curr[1], $curr) = ($_, $$curr[0]);
}
__DATA__
foo
bar
baz
biz
buz
goo
car
caz
ciz
cuz
P.S. If anyone knows how to reduce the two-line circular linked list definition into one line of idiomatic perl I would love to see it. I'm too tired to think of something clever at the moment. | [reply] [Watch: Dir/Any] [d/l] |
|
use strict;
use warnings;
my $lookback = 3;
my $match = qr/\wiz/;
my @lbuff;
while (<DATA>) {
/$match/ and defined $lbuff[ $. % $lookback ] and print "$lbuff[ $
+. % $lookback ]";
$lbuff[ $. % $lookback ] = $_;
}
__DATA__
foo
bar
baz
biz
buz
goo
car
caz
ciz
cuz
| [reply] [Watch: Dir/Any] [d/l] |
|
compared to other proposals is the pushing and shifting which involves a lot of copying data around. Sure it doesn't :)
This can be done more easily by cycling through the buffer using the modulo operator on the line number of the data. If all the data you want is at the end
my @data = ( undef, undef, qw/ bingo nameo / );
print @data[ -1,-2,-3 ];
__END__
nameobingo
| [reply] [Watch: Dir/Any] [d/l] |
|
|
#!/usr/bin/env perl
use Modern::Perl;
my $lb = 3; # lookback
my $match = qr/\wiz/;
my @buff;
while ($buff[$.%($lb+1)] = ($_ = <DATA>)) {
my $lbi = ($.+1)%($lb+1); # lookback index
print $buff[$lbi] if defined $buff[$lbi] and /$match/;
}
__DATA__
foo
bar
baz
biz
buz
goo
car
caz
ciz
cuz
P.S. Thanks for optimizing my two line circular linked list declaration to zero lines! ;-)
P.P.S. Too bad: Yours is, IMHO, the best answer to the OP, but is buried in this subthread.
EDIT Just reviewed this code and saw that I forgot defined! This opens the door for error if the lookback line is empty or a zero. While I was at it, I changed the emphasis from the lookback line itself to the index that finds it; plus, no more relying on side effects in the conditional.
For the three of y'all who upvoted the earlier incarnation, the original code is below. One line was deleted, one line added, one line changed.
| [reply] [Watch: Dir/Any] [d/l] [select] |
Re: Printing line before matching expression
by uday_sagar (Scribe) on Sep 11, 2013 at 09:28 UTC
|
open (INPUT, $ARGV[0]) or die "I couldn't get at input text";
open (SAME_INPUT, $ARGV[0]) or die "I couldn't get at input text";
open (OUTPUT, '>outfile.txt') or die "Can't write to outfile: $!";
while ($line = <INPUT>) {
if (( $line =~ /index_1/ ) and ($line =~ /6/)) {
$index = $.-2;
}
}
while ($line = <SAME_INPUT>) {
if ($. == $index) {
print OUTPUT $line;
}
}
| [reply] [Watch: Dir/Any] [d/l] |
|
open my $INPUT, "<", $ARGV[0] or die "I couldn't get at input text";
open my $SAME_INPUT, "<", $ARGV[0] or die "I couldn't get at input tex
+t";
my $offset = $ARGV[1];
my $line = <$INPUT> for (1..$offset); # discard the n first lines, can
+'t do anything with them anyway
while (<$INPUT>) {
$line = <$SAME_INPUT>;
print $line if /$regex/;
}
There is also the alternative of slurping the file into an array and walking through the array. Reading the minus $n line is then trivial.
| [reply] [Watch: Dir/Any] [d/l] |
|
Yes, concept of offset is good! :-)
| [reply] [Watch: Dir/Any] |