Reputation: 78
I'm trying to modify content of a file using Perl.
The following script works fine.
#!/usr/bin/perl
use strict;
use warnings;
open(FH,"test.txt") || die "not able to open test.txt $!";
open(FH2,">","test_new.txt")|| die "not able to opne test_new.txt $!";
while(my $line = <FH>)
{
$line =~ s/perl/python/i;
print FH2 $line;
}
close(FH);
close(FH2);
The content of test.txt
:
im learning perl
im in File handlers chapter
The output in test_new.txt
:
im learning python
im in File handlers chapter
If I try to use same file handle for modifying the content of file, then I'm not getting expected output. The following is the script that attempts to do this:
#!/usr/bin/perl
use strict;
use warnings;
open(FH,"+<","test.txt") || die "not able to open test.txt $!";
while(my $line = <FH>)
{
$line =~ s/perl/python/i;
print FH $line;
}
close(FH);
Incorrect output in test.txt
:
im learning perl
im learning python
chapter
chapter
How do I modify the file contents using single file handle?
Upvotes: 0
Views: 439
Reputation: 385657
You can't delete from a file (except at the end).
You can't insert characters into a file (except at the end).
You can replace a character in a file.
You can append to a file.
You can shorten a file.
That's it.
You're imagining you can simply replace "Perl" with "Python" in the file. Those aren't of the same length, so it would require inserting characters into the file, and you can't do that.
You can effectively insert characters into a file by loading the rest of the file into memory and writing it back out two characters further. But doing this gets tricky for very large files. It's also very slow since you end up copying a (possibly very large) portion of the file every time you want to insert characters.
The other problem with in-place modifications is that you can't recover from an error. If something happens, you'll be left with an incomplete or corrupted file.
If the file is small and you're ok with losing the data if something goes wrong, the simplest approach is to load the entire file into memory.
open(my $fh, '<+', $qfn)
or die("Can't open \"$qfn\": $!\n");
my $file = do { local $/; <$fh> };
$file =~ s/Perl/Python/g;
seek($fh, 0, SEEK_SET)
or die $!;
print($fh $file)
or die $!;
truncate($fh)
or die $!;
A safer approach is to write the data to a new file, then rename the file when you're done.
my $new_qfn = $qfn . ".tmp";
open(my $fh_in, '<', $qfn)
or die("Can't open \"$qfn\": $!\n");
open(my $fh_out, '<', $new_qfn)
or die("Can't create \"$new_qfn\": $!\n");
while (<$fh_in>) {
s/Perl/Python/g;
print($fh_out $_);
}
close($fh_in);
close($fh_out);
rename($qfn_new, $qfn)
or die $!;
The downside of this approach is it might change the file's permissions, and hardlinks will point to the old content instead of the new file. You also need permissions to create a file.
Upvotes: 2
Reputation: 108
As @Сухой27 answered
it's typical situation that perl onliner pleasingly used.
perl -i -pe 's/perl/python/i'
perl
takes below options
-p
: make line by line loop(every line assign into $_
and print after evaluated $_
)-e
: evaluate code block in above loop ( regex take $_
as default operand )-i
: in plcae file edit (if you pass arguments for -i, perl preserve original files with that extention)if you run below script
perl -i.bak -pe 's/perl/python/i' test.txt
you will get modified test.txt
im learning python
im in File handlers chapter
and get original text files named in test.txt.bak
im learning perl
im in File handlers chapter
Upvotes: 0