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

I want to read in a file like
*********************** word1 word2 word4 word3 word4 word5 word6 word7 word8 word9 word10 word11 word12 word13 ************************
where words are not necesarily the same. I want to edit the first line, only if the second line has a specific word in it. I can fall through every line in the file with while <> and check every line but I cannot then backtrack to the previous line to edit it. Is there a way???

Replies are listed 'Best First'.
Re: edit text
by mirod (Canon) on Dec 12, 2001 at 16:24 UTC

    You just have to buffer the last line you read and output it only after editing (if it needs to be edited):

    #!/bin/perl -w use strict; my $last_line=''; while( <DATA>) { chomp; if( m{\bedit\b}) { # edit to taste $last_line .= ' # edited'; } print $last_line, "\n"; $last_line= $_; # buffer the line } print $last_line, "\n"; __DATA__ *********************** word1 word2 word4 word3 word4 word5 word6 word7 word8 word9 edit word10 word11 word12 word13 edit ************************

    Note that this way you only have 2 lines in memory at all time, which can be useful if you are processing a hige file.

Re: edit text
by davorg (Chancellor) on Dec 12, 2001 at 16:26 UTC

    Perhaps some kind of two item queue.

    my @queue; # prime @queue with first line of data push @queue, scalar <DATA>; while (<DATA>) { push @queue, $_; if (/your condition/) { # edit line in $queue[0] } print shift @queue; } # print remaining queue print @queue;
    --
    <http://www.dave.org.uk>

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

Re: edit text
by tachyon (Chancellor) on Dec 12, 2001 at 16:20 UTC

    Yes of course there is a way. Try this:

    open FILE, "<whatever.txt" or die "Oops $!\n"; my @content = <FILE>; close FILE; for my $i ( 0 .. $#content -1 ) { $content[$i] = $some_edit if $content[$i+1] eq $some_next_line_bit +; } open FILE, ">whatever.txt" or die "Oops $!\n"; print FILE @content; close FILE;

    There are plenty of other ways to do it. You could revise the code above using the +< operator to open the file for R/W and use a seek FILE, 0, 0 to reset the pointer for the write to save a couple of lines. If the file is huge you could use a while loop and temp file thusly:

    my $last = ''; open FILE, "<whatever.txt" or die "Oops $!\n"; open TMP, ">tmp.txt" or die "Oops $!\n"; while (<FILE>) { $last =~ s/this/that/ if $_ eq $some_next_line_bit; print TMP $last; $last = $_; } print TMP $last; # print the very last line close FILE; close TMP; rename "tmp.txt", "whatever.txt"; unlink "tmp.txt";

    cheers

    tachyon

    s&&rsenoyhcatreve&&&s&n.+t&"$'$`$\"$\&"&ee&&y&srve&&d&&print

Re: edit text
by derby (Abbot) on Dec 12, 2001 at 21:36 UTC
    poir,
    As others have pointed out, you probably need to do some type of "buffering." There is a slightly novel recipe (14.7) in the cookbook that outlines an alternative most askers of this question would probably like.

    to paraphrase 14.7

    #!/usr/bin/perl use DB_File; my $file = shift; print "usage $0 filename" unless $file; tie( @array, "DB_File", $file, O_RDWR, 0666, $DB_RECNO ) || die "Cannot open file $file ($!)\n"; if( $array[1] =~ /word4/ ) { $array[0] = "this line now changed"; } untie @array;

    A good way to waste ... er learn some perl would be to benchmark this approach against one of the other buffering approaches.
    -derby