Beefy Boxes and Bandwidth Generously Provided by pair Networks
Think about Loose Coupling
 
PerlMonks  

Finding part of a file

by willa (Acolyte)
on Dec 04, 2001 at 15:19 UTC ( [id://129300]=perlquestion: print w/replies, xml ) Need Help??

willa has asked for the wisdom of the Perl Monks concerning the following question:

Is it possible to find part of a file, between two given lines?
For example, in the following file:
apple banana pear peach grape orange
Can you just give it banana and grape and have it return pear and peach?

Replies are listed 'Best First'.
Re: Finding part of a file
by davorg (Chancellor) on Dec 04, 2001 at 16:00 UTC

    Expanding on blakem's suggestion, here's what I'd do:

    #!/usr/bin/perl -w use strict; my ($start, $end) = qw(banana grape); while (<DATA>) { if (/^$start$/ .. /^$end$/) { print unless /^($start|$end)$/; } } __END__ apple banana pear peach grape orange

    Update: Thanks to blakem for pointing out the missing $ on the end of the flip-flop. He also points out that this may be a good place to use the /o option to the match operator and I agree. You should probably benchmark to see how much of a gain you get.

    --
    <http://www.dave.org.uk>

    "The first rule of Perl club is you do not talk about Perl club."
    -- Chip Salzenberg

      I don't know if this is any better, but just for variety (using an extra variable, but shorter match pattern):
      if (my $status = /^$start$/ .. /^$end$/) { print unless $status =~ /^1$|E/; } # Or no extra variable, but getting more obfuscated :) print if (/^$start$/../^$end$/) !~ /^1?$|E/;
        Nifty... I didn't know that the flip flop returned anything other than 1 or 0.
        % perl -lne 'printf "%-6s %s\n", $_, scalar(/^banana$/ .. /^grape$/) +' fruit.txt apple banana 1 pear 2 peach 3 grape 4E0 orange
        So, I can tweak my one-liner to exclude the endpoints like so:
        % perl -ne 'print unless (/^banana$/ .. /^grape$/) =~ /^1?$|E/' fruit +.txt pear peach
        Update:
        Doh!, runrigs second stanza was added while I was replying.... I guess obfuscated minds think alike. ;-)

        -Blake

Re: Finding part of a file
by blakem (Monsignor) on Dec 04, 2001 at 15:43 UTC
    This one-liner will get you most of the way there... though the endpoints (banana and grape) are included in the output as well...
    % perl -ne 'print if /^banana$/ .. /^grape$/' fruit.txt banana pear peach grape % cat fruit.txt apple banana pear peach grape orange

    -Blake

      Are we allowed to assume a unix box?

      awk -e '/banana/,/grape/' fruit.txt

      Its always a good idea to remember where these good ideas come from :-)

      One side-effect of learning perl is that you become unable to write non-trivial awk, IME.

        I like sed:

        sed -n '/banana/,/grape/p' fruit.txt

        but find that almost anything I could do in sed, I would prefer to do in Perl. :-)

        Impossible Robot
Re: Finding part of a file
by broquaint (Abbot) on Dec 04, 2001 at 16:42 UTC
    You could refer to good ol' regex if the flip-flop operator scares
    use strict; my ($start, $end) = ("bar".$/, $/."xxx"); my $data = join "", <DATA>; my ($inbetween) = $data =~ /$start(.*?)$end/s; print $inbetween, $/; __DATA__ foo bar baz quux zab xxx foo
    While not the most elegant of solutions (unless you're regex mad ;o) a little TMTOWTDI never hurt anyone, right?
    HTH

    broquaint

      my $begin = shift @ARGV; my $end = shift @ARGV; $_=<> until /^$begin$/; while (<>) { last if /^$end$/; print; }
      This is what struck me as the most obvious solution, but, of course, there's more than one obvious way to do it...
        You can nest reads on an open filehandle. I didn't know
        this until looking at your code and playing with it a little;
        here's what I got.
        #!/usr/bin/perl $begin='banana'; $end='grape'; while(<DATA>) { /$begin/ ? eval { /$end/ ? last : print while (<DATA>) } : next } __DATA__ apple banana pear peach grape prange
        blyman
Re: Finding part of a file
by mce (Curate) on Dec 04, 2001 at 15:49 UTC
    Hi,

    I would suggest to play around with the $/ variable (i.e. line seperator). You can do it like this.

    use strict; use warnings; { local $/=""; <DATA> =~ /banana\n(.*)\ngrape\n/s; print $1; } __DATA__ apple banana pear peach grape orange

    The local is not needed in this case, but it is a good habit to use it since you are resetting perl internal variables.
    This prints

    pear
    peach
    
    
    I hope this helps,
    ---------------------------
    Dr. Mark Ceulemans
    Senior Consultant
    IT Masters, Belgium
      At Mr. Ceulemans reply, I'd like to show up a different way, without the use of $1:
      { local $/ = undef; my ($foundString) = ( <DATA> =~ /\bbanana\n(.*)\ngrape\n/s ); print $foundString; } __DATA__ apple banana pear peach grape orange
      The problem with this code might be multivalued lines (because of \b)... Best regards, Strat
Re: Finding part of a file
by George_Sherston (Vicar) on Dec 04, 2001 at 15:31 UTC
    update: changed chop to chomp on Aighearach's suggestion, which is of course very sensible, as one doesn't know for sure that the record separator will only be one character.

    AFAIK you have to read through the file. You can only pull out a pre-determined fragment of the file if you know its location in bytes. Then you would use seek and read. But unless it's a huge file, there's no harm in doing something like
    my $start = 'banana'; my $end = 'grape'; my $get = 0; my @results; open FILE, "file.txt"; while (<FILE>) { chomp; $get = 0 if $_ eq $end; push @results, $_ if $get; $get = 1 if $_ eq $start; } print $_,"\n" for @results;
    You'd need something a little more subtle if you aren't sure that 'banana' comes before 'grape' or you think one or other might be absent altogether.

    § George Sherston
      You might want to consider chomp instead of chop
      --
      Snazzy tagline here
Re: Finding part of a file
by mkmcconn (Chaplain) on Dec 05, 2001 at 14:22 UTC

    Bored waiting for installs to finish. Thanks for an entertaining question:

    #!/usr/bin/perl -wl # gate : a printer gate use strict; use Getopt::Std; use constant OPEN => 1; use constant SHUT => 0; use vars qw($opt_p $opt_o $opt_s $opt_b $opt_l); local ($opt_p, $opt_o, $opt_s, $opt_b, $opt_l); getopts(qw(pb:o:s:l:)); unless ($opt_o or $opt_b ){ usage() unless $opt_p; } if (@ARGV){ open FH, $ARGV[0] or die $!; } else { *FH = \*DATA ; } my $enter = $opt_p ? $opt_p : SHUT; # breeze my $breach = $opt_b ? $opt_b : 'the burgler breaks in'; my $open = $opt_o ? $opt_o : 'the butler opens the door'; my $shut = $opt_s ? $opt_s : 'the bouncer shuts the door'; my $last = $opt_l ? $opt_l : 'the bellhop is last through the door +'; while (my $door = $_ = <FH>){ chomp ($door,$_); exit if $door =~ /^$shut$/; # bouncer $enter = OPEN if $door =~ /^$breach$/; # burgler $enter and print; $enter = OPEN if $door =~ /^$open$/; # butler exit if $door =~ /^$last$/; # bellhop } sub usage { print "The door is shut\n", "$0 -p (p_neuma is Greek for 'breeze') -b (burgler b_reaks in) <item> -o (butler o_pens the door) <item> -s (bouncer s_huts the door) <item> -l (bellhop is l_ast through the door)<item> "; die"\n"; } __END__ apple banana pear peach grape orange

    $ gate -o banana -s grape
    pear
    peach
    update2
    $ gate -o apex -s apple ./words
    apexed
    ...
    applausively
    mkmcconn

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://129300]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others meditating upon the Monastery: (4)
As of 2024-03-28 23:04 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found