In my code below I am trying to replace the pattern of "---- ------" with the corresponding number of spaces. When I run the code, all the file contents are wiped out. I cannot figure out why. I have the sample text below where the replacements are to be done. I would appreciate assistance.
#!/usr/bin/perl -w
use strict;
use warnings;
use File::Path;
use File::Copy;
use feature 'unicode_strings';
use utf8;
my $IN;
my $dir;
opendir $dir, $Dirs[$mm] or die "opendir failed on $Dirs[$mm]: $! ($^E)";
while (my $file = readdir $dir) { #reading input directory
next if -d $file;
if ($file =~ /Ranked/) {
print "$file\n";
open ($IN, " > $Dirs[$mm]/$file") or die "open '$file': failed $! ($^E)";
while (<$IN>) {
s/---- ------/ /g;
print "$_\n";
}
close $dir;
exit;
}
}
END
Ranked Rainfall: Jun
********************
CHIPAT01 CHIPEP01 CHOMA001 ISOKA001 KABOMP01 KABWE001 KABWE002 KAFIRO01 KAFUE001 KALABO01 KAOMA001 KASAMA01 KASEMP01
Year Rnfl Year Rnfl Year Rnfl Year Rnfl Year Rnfl Year Rnfl Year Rnfl Year Rnfl Year Rnfl Year Rnfl Year Rnfl Year Rnfl Year Rnfl 1968 17.0 2003 13.2 2000 29.2 2004 3.1 1988 13.3 1988 2.8 ---- ------ 1967 2.8 1966 1.3 2009 2.3 1965 10.7 1968 11.9 1988 6.0 1983 5.8 2011 6.1 1966 14.0 2002 2.0 1965 9.4 1960 0.5 ---- ------ ---- ------ 1960 0.6 ---- ------ 2009 1.8 1952 0.8 1940 5.1 1974 4.6 1996 0.6 1997 4.5 ---- ------ 1966 2.3 1986 0.1 ---- ------ ---- ------ ---- ------ ---- ------ 1966 0.3 1936 0.3 1966 4.8 1960 3.3 2007 0.5 1988 4.0 ---- ------ Ave 0.5 Ave 0.1 ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ Ave 0.2 1965 1.8 1999 0.3 2002 3.7 ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ 1948 1.6 Ave 0.1 2003 3.6 ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ 1971 1.0 ---- ------ 1955 3.3 ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ 1978 0.9 ---- ------ 1976 2.5 ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ 1961 0.5 ---- ------ 1975 1.6 ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ 1946 0.5 ---- ------ 1978 1.6 ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ 1967 0.5 ---- ------ 2011 1.4 ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ Ave 0.4 ---- ------ 1960 1.3 ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ 1964 0.3 ---- ------ 2009 1.0 ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ 1996 0.6 ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ 1969 0.6 ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ Ave 0.5 ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ 1958 0.5 ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ 1979 0.3 ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------ ---- ------
CodePudding user response:
Reading and writing to the same file at the same time requires some care. Here, you are using > to read and write to $file at the same time. However, the documentation of open warns that:
You can put a
in front of the>or<to indicate that you want both read and write access to the file; thus<is almost always preferred for read/write updates--the>mode would clobber the file first
Instead, you should write to a temporary file. Once you're done writing, you can replace the original file with the temporary one. To open a temporary file, you can use the File::Temp module. To replace the original file with the new one, you can use move from the File::Copy module. Your code corrected:
use File::Temp qw(tempfile);
use File::Copy;
while (my $file=readdir $dir) {
next if -d "$dir/$file";
if($file =~ /Ranked/){
print "$file\n";
my ($out_fh, $out_filename) = tempfile();
open (my $IN, "<", "$Dirs[$mm]/$file") or die "open '$file': failed $! ($^E)";
while( <$IN> ){
s/---- ------/ /g;
print $out_fh $_;
}
close $out_fh;
copy($out_filename, "$Dirs[$mm]/$file");
}
}
Note that it's important to close $out_fh before copying it.
Small tips:
next if -d $fileshould benext if -d "$dir/$file".You should declare your variables in the narrowest scope possible. This means that instead of starting your script with
my $IN;and later doopen $IN, ..., you should doopen my $IN, .... Same thing formy $dirandopendir.
Alternative solutions:
Perl has a built-in mechanism for in-place file editing, but it's more suitable for one-liners than for full scripts. This is enabled by the
-icommand-line switch, but it can also be used outside of one-liners if needed.Some CPAN packages (such as
File::Inplacecan be used to avoid having to write the wholetempfile/moveboilerplate yourself.
