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

Hi All.

I just received this great piece of code below from a friend. It writes a line to to a specific place in an external file. Problem is each time it runs it adds another line just under the former line. Would anyone know how to modify this code to "replace" the old line with the new, rather than just keep adding new lines? Any advice would be deeply appreciated! Thanks!

Here is the code:
#!/usr/local/bin/perl use strict; use CGI ':standard'; print "Content-type: text/html\n\n"; my @array; # this opens the original file and sets up the # external file as an array open (FILE,"external_file.txt"); @array = <FILE>; close(FILE); # initializes loop count at 0 my $count = 0; #loops through the array (used to be the external # file) foreach (@array) { # the number (3 in his case) corresponds to the # line number where you want to add the #statement in quotation marks if ($count == 3) { #adds the statement in parenthesis to the array #(file) using the concatenation operator (the #period) $_ = $_ . "my newline goes here\n"; #use this line if you want to add a space before #the newline # $_ = $_ . "\nmy newline goes here\n"; #this prints the entire array (with the line #added) to a temporary file open (FILE,">>external.txt"); print FILE @array; close (FILE); #this renames the temporary file to the original #file name but with new line added rename ("external.txt","external_file.txt"); exit; } #endif #loop counter $count++; } #endforeach # exits the program just in case there are less # than the specified number of lines in # the original external file,if ($count == x) exit;

edited: Tue Oct 15 17:33:03 2002 by jeffa - code tags s/<br>//g

Replies are listed 'Best First'.
Re: Replace a line with a new one
by Aristotle (Chancellor) on Oct 15, 2002 at 09:05 UTC
    It writes a line to to a specific place in an external file. Problem is each time it runs it adds another line just under the former line.
    That's because you didn't remove the newline that was there. See perldoc -f chomp. And Zaxo is right, you shouldn't loop the entire array when you already know the index. Given that, the foreach loop becomes:
    chomp $array[3]; $array[3] .= "my newline goes here\n";
    As fruiture already said, I propose you use Tie::File. You don't need to do all the open/read/close hoopla yourself, plus it doesn't slurp the file so it will be more efficient. That's especially important when writing CGI scripts, where usually every byte of memory and every millisecond saved counts.

    Makeshifts last the longest.

Re: Replace a line with a new one
by nothingmuch (Priest) on Oct 14, 2002 at 21:03 UTC
    Several problems arise from the code. The first - scalability - reading a whole file into an array is kinda asking for trouble, you might run out of memory, and it'll probably take a longer while.
    Another is that you're opening the temporary file for appending. Assuming the file is new, this shouldn't matter to you, but if a file by that name exists, you just added the new text to it's end, instead of rewriting from scratch. See perlopentut for a tutorial on openning files.
    Yet another, which directly confronts your problem, is that the readline operator does not remove the input record seperator ($/, which is usually "\n") from read lines.
    reading a file which contains "foo\nbar\n" into an array will yield ("foo\n","bar\n").
    When you join $_ with your string, it'll keep the line break you put in, making $_ contain two lines.
    To get around that line break you need to use chop, which takes off the last character of a string, or chomp which only removes an occurance of $/ from a string.
    I think the wisest approach would be something like this:
    open (FILE,"external_file.txt"); open (TEMP,">external_file.txt.tmp"); my $i = 0; while (<FILE>){ # magical - puts every line in $_, until the end of fi +le is reached if ($i == 3){ # if $i is the 3rd line chomp $_; # remove the line break at the end of $_ $_ .= "my new line goes here\n"; # add the string } print TEMP $_; # print the line you just read to the temporary fil +e } continue { # performed at the end of every loop iteration $i++; } close FILE; close TEMP; unlink ("external_file.txt"); # incase rename does not clobber, delete + the original rename ("external_file.txt.tmp","external_file.txt");
    -nuffin zz zZ Z Z #!perl
      Thanks so much for the good advice.
      I just tried your code and it works well - however - it behaves the same as the original in that it just keeps adding another line every time it runs - instead of overwriting the previous line written. It appends another line right behind the previous one.
      The other code added a new line just under the previous line.
      Any further thoughts?
      Thanks again for responding.
        Ah, ok... I understood from the code that you are trying to join the strings, from the concatenation operator ^_^. I should make it a habit to make notice of the english...
        $_ = "my newline\n" if ($i == 3); print TEMP $_;
        hope this works...

        -nuffin zz zZ Z Z #!perl
Re: Replace a line with a new one
by Zaxo (Archbishop) on Oct 14, 2002 at 20:41 UTC
    $array[3] = "The new text\n";

    Why does your code loop through the whole array when the index is known?

    After Compline,
    Zaxo

Re: Replace a line with a new one
by kabel (Chaplain) on Oct 14, 2002 at 21:02 UTC
    for this type of file processing i love to use Tie::File

    then the file is merely a perl array and as such can be operated with splice to dwym. :)
Re: Replace a line with a new one
by Steve_p (Priest) on Oct 14, 2002 at 20:41 UTC
    To just write the data to a file and overwrite what's in there, replace
    open (FILE,">>external.txt");
    with
    open (FILE,">external.txt");
      That's a good thought, Steve. Unfortunately, that does not work. The data is added to the old file before it is written into "external.txt" - as "external.txt" is just the temporary file. Wish it were that simple. Thanks for responding, however. I appreciate it a lot!