Beefy Boxes and Bandwidth Generously Provided by pair Networks
"be consistent"
 
PerlMonks  

The easiest way to substitute a part of the content of a file.

by MF (Scribe)
on Jan 03, 2001 at 03:04 UTC ( #49413=perlquestion: print w/replies, xml ) Need Help??

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

What is the easiest/best way to do this? The simplest thing that occured to me,

open(FILE,"fileloc"); while (<FILE>) { s/content/newcontent; }

doesn't work. (Ignore the fact that the construct is wrong, the file just doesn't open to read and write at the same time.)

Reading the file contents into an array, modifying the array and rewriting the file is what I finally did, but replacing the entire thing instead of appending a small part (The idea is finding a match, replace it with something followed by reinserting the match) means more code and more processing time.

This may sound dumb and my solution is probably twice as dumb as this question but that's exactly the reason of this questions existence.

Replies are listed 'Best First'.
Re (tilly) 1: The easiest way to substitute a part of the content of a file.
by tilly (Archbishop) on Jan 03, 2001 at 03:07 UTC
    For a short one-off I would recommend using the switches out of perlrun:
    perl -pi.bak -e 's/content/newcontent/g' fileloc
    (Use " instead of ' on Windows.)

    For longer programs, you really do need to read it, manipulate a string, then write it again.

Re: The easiest way to substitute a part of the content of a file.
by turnstep (Parson) on Jan 03, 2001 at 03:55 UTC

    If the part of the file you are going to be replacing is at the end, you can increase speed and efficiency by not writing to the disk until you get to the exact part of the file where you have made the change. Once you make a change, however, you'll have to write everything else from memory to the disk. This is basically because when you change a file, you change it's length. (If you are simply subsituting one letter for another, it's possible (though a bit foolhardy, IMO) to open it for read/write, find and make your changes, and close the file, as the size will remain the same) For a more normal, not-as-foolhardy case, use something like this:

    ## Open our file for reading and writing: open(MYFILE, "+< $myfile") or die "Could not open $myfile: $!\n"; ## File locking would be a Good Idea here ## Loop through the file one line at a time while(<MYFILE>) { if (/$foo/) { ## We have a match! my $tellme = tell(MYFILE)-length $_; ## See below my @baz = <MYFILE>; ## Slurp everything from that point forward unshift(@baz,$_); ## Throw our string back in ## Above, we stored the location in the file where the ## first line was that matched. Now we rewind to that point. seek(MYFILE,0,$tellme); ## The file from where we are (via seek) to the end of ## the file is now in memory, inside @baz for (@baz) { s/$foo/$bar/; print MYFILE $_; } ## Now we must cleanup our file, especially if the ## file has shrunk from our changes: truncate (MYFILE,tell(MYFILE)); last; ## Bail from the original MYFILE loop } }
    This is kind of a pain (obviously) but really can save you memory if the file is large or your RAM is small. The above is also a bit verbose - you can remove a lot of the "$_" and "MYFILE"s from above - see the documentation on seek, truncate, and tell for more.

Re: The easiest way to substitute a part of the content of a file.
by MF (Scribe) on Jan 03, 2001 at 04:52 UTC
    Thank you for pointing that out :)

    Unfortunately, Tilly, it's a program that needs to run more than once and is quite large. I didn't know that was possible though :).

    Turnstep, it is indeed at the end of a file. Your example doesn't deviate much from my solution save for the fact that it doesn't put all that crap before the part to be substituted in memory. With 12 MB of RAM (edo!) in a crappy old machine I ought to replace it's quite important ;].

    Anyways, thanks go to both of you.

      I like turnstep's editing the file in place, but it seems to me, if you're short on RAM, the best thing to do would be to rename the old file, open it to read and another to write.

      rename $filename, $filename.".bak" or die "$!"; open(IN, $filename.".bak") or die "$!"; open(OUT, ">".$filename) or die "$!"; while (<IN>) { s/content/newcontent/g; print OUT; } close OUT; close IN;

      Update

      It sounds like the original poster is saying that "perl -p/n -i" won't work because this is a part of a larger program. Nevertheless, you make a good point tye. So I now propose:

      open(IN, $filename) or die "$!"; open(OUT, ">".$filename.".new") or die "$!"; while (<IN>) { s/content/newcontent/g; print OUT; } close OUT; close IN; rename $filename.".new", $filename or die "$!";

      You might even toss in a flock somewhere to try to guarantee exclusivity.

        That is roughtly what tilly's "perl -pi" solution does. And I would discourage rewriting the existing file without renaming, that way lies nasty race conditions. Best to write a new file and rename it into place after you are done (you still have a race condition if you have simultaneious writers, but are in good shape in the more common case of a single writer and any number of readers).

                - tye (but my friends call me "Tye")
Re: The easiest way to substitute a part of the content of a file.
by tphyahoo (Vicar) on Dec 09, 2005 at 13:51 UTC
    For a really really really basic introduction to "substitute pie" (and some other basic perlrun functionality) see A gentle introduction/tutorial to the perl command line flags.. By "substitute pie", I mean what Tilly was talking about above. (This writeup skips the "i" (in place) part of that recipe, but hopefully still helps.)

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others surveying the Monastery: (4)
As of 2021-03-03 15:31 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    My favorite kind of desktop background is:











    Results (80 votes). Check out past polls.

    Notices?