Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I print a matching line, one line immediately above it and one line immediately below?

Tags:

perl

From a related question asked by Bi, I've learnt how to print a matching line together with the line immediately below it. The code looks really simple:

#!perl
open(FH,'FILE');
while ($line = <FH>) {
    if ($line =~ /Pattern/) {
        print "$line";
        print scalar <FH>;
    }
}

I then searched Google for a different code that can print matching lines with the lines immediately above them. The code that would partially suit my purpose is something like this:

#!perl

@array;
open(FH, "FILE");
while ( <FH> ) {
  chomp;
  $my_line = "$_";
  if ("$my_line" =~ /Pattern/) {
      foreach( @array ){
          print "$_\n";
      }
      print "$my_line\n"
  }
  push(@array,$my_line);
  if ( "$#array" > "0" ) {
    shift(@array);
  }
};

Problem is I still can't figure out how to do them together. Seems my brain is shutting down. Does anyone have any ideas?

Thanks for any help.

UPDATE:

I think I'm sort of touched. You guys are so helpful! Perhaps a little Off-topic, but I really feel the impulse to say more.

I needed a Windows program capable of searching the contents of multiple files and of displaying the related information without having to separately open each file. I tried googling and two apps, Agent Ransack and Devas, have proved to be useful, but they display only the lines containing the matched query and I want aslo to peek at the adjacent lines. Then the idea of improvising a program popped into my head. Years ago I was impressed by a Perl script that could generate a Tomeraider format of Wikipedia so that I can handily search Wiki on my Lifedrive and I've also read somewhere on the net that Perl is easy to learn especially for some guy like me who has no experience in any programming language. Then I sort of started teaching myself Perl a couple of days ago. My first step was to learn how to do the same job as "Agent Ransack" does and it proved to be not so difficult using Perl. I first learnt how to search the contents of a single file and display the matching lines through the modification of an example used in the book titled "Perl by Example", but I was stuck there. I became totally clueless as how to deal with multiple files. No similar examples were found in the book or probably because I was too impatient. And then I tried googling again and was led here and I asked my first question "How can I search multiple files for a string pattern in Perl?" here and I must say this forum is bloody AWESOME ;). Then I looked at more example scripts and then I came up with the following code yesterday and it serves my original purpose quite well:

The codes goes like this:

#!perl

$hits=0;
print "INPUT YOUR QUERY:";
chop ($query = <STDIN>);
$dir = 'f:/corpus/'; 
@files = <$dir/*>;
foreach $file (@files) {
open   (txt, "$file");

while($line = <txt>) {
if ($line =~ /$query/i) {   
$hits++;
print "$file \n $line";     
print scalar <txt>;
}
}
}
close(txt);
print "$hits RESULTS FOUND FOR THIS SEARCH\n";

In the folder "corpus", I have a lot of text files including srt pdf doc files that contain such contents as follows:

Then I dumped the body.

J'ai mis le corps dans une décharge.

I know you have a wire.

Je sais que tu as un micro.

Now I'll tell you the truth.

Alors je vais te dire la vérité.

Basically I just need to search an English phrase and look at the French equivalent, so the script I finished yesterday is quite satisfying except that it would to be better if my script can display the above line in case I want to search a French phrase and check the English. So I'm trying to improve the code. Actually I knew the "print scalar " is buggy, but it is neat and does the job of printing the subsequent line at least most of the time). I was even expecting ANOTHER SINGLE magic line that prints the previous line instead of the subsequent :) Perl seems to be fun. I think I will spend more time trying to get a better understanding of it. And as suggested by daotoad, I'll study the codes generously offered by you guys. Again thanks you guys!

like image 906
Mike Avatar asked Nov 27 '22 00:11

Mike


2 Answers

It will probably be easier just to use grep for this as it allows printing of lines before and after a match. Use -B and -A to print context before and after the match respectively. See http://ss64.com/bash/grep.html

like image 128
Brian Rasmussen Avatar answered Jun 23 '23 12:06

Brian Rasmussen


Here's a modernized version of Pax's excellent answer:

use strict;
use warnings;

open( my $fh, '<', 'qq.in') 
    or die "Error opening file - $!\n";

my $this_line = "";
my $do_next = 0;

while(<$fh>) {
    my $last_line = $this_line;
    $this_line = $_;

    if ($this_line =~ /XXX/) {
        print $last_line unless $do_next;
        print $this_line;
        $do_next = 1;
    } else {
        print $this_line if $do_next;
        $last_line = "";
        $do_next = 0;
    }
}
close ($fh);

See Why is three-argument open calls with lexical filehandles a Perl best practice? for an discussion of the reasons for the most important changes.

Important changes:

  • 3 argument open.
  • lexical filehandle
  • added strict and warnings pragmas.
  • variables declared with lexical scope.

Minor changes (issues of style and personal taste):

  • removed unneeded parens from post-fix if
  • converted an if-not contstruct into unless.

If you find this answer useful, be sure to up-vote Pax's original.

like image 44
3 revs Avatar answered Jun 23 '23 11:06

3 revs