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. | [reply] [Watch: Dir/Any] [d/l] |
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.
| [reply] [Watch: Dir/Any] [d/l] |
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.
| [reply] [Watch: Dir/Any] |
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.
| [reply] [Watch: Dir/Any] [d/l] [select] |
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")
| [reply] [Watch: Dir/Any] |
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.) | [reply] [Watch: Dir/Any] |