jagtar has asked for the wisdom of the Perl Monks concerning the following question:
Hello,
I am trying to make inline changes to the file(passed using Glob)
I am doing like this, but i think it is not the best way to do it.
In below code i have to use stdout redirection to a new local file, which i don't want to do.
I need to make changes to the same file being passed by rm_spaces(\*FILE)
open(STDOUT, "+>csv_add_up");
rm_spaces(\*CSVADD_FILE);
close STDOUT;
sub rm_spaces {
my $file = shift;
while (<$file>) {
chomp;
my @fields = split /,/;
s/\s+//g for @fields;
s/\/I\/0//g for @fields;
s/\/T\/0//g for @fields;
print join ",", @fields,"\n";
}
}
Thanks,
Jagtar
Re: Inline change to the file passed by Glob
by haukex (Archbishop) on Aug 23, 2017 at 05:35 UTC
|
make inline changes ... open(STDOUT, "+>csv_add_up")
Are you sure you want to do it this way? An open mode of +> clobbers the file first, I don't think that's what you want. Also you don't have error checking (as in open ... or die "$filename: $!";).
i have to use stdout redirection
Normally, you would do something like open my $out_fh, '>', $filename or die "$filename: $!"; and then print $out_fh "output\n";. Alternatively one could use select, but the disadvantage is that it redirects all print and related statements. Re-opening STDOUT is something one should do only if you know what you are doing and why. (Update: If you really needed to capture STDOUT, which I don't think is the case here, use Capture::Tiny.)
passed using Glob
Is this a requirement? (I'm asking because I wouldn't necessarily do it this way.)
Anyway, the usual way to make "inline" changes is to either open a temporary file, write to it, and then rename the temporary file over the original, or the way Perl's -i switch does it, by renaming the original and then opening a new file with the same name as the original. It's theoretically possible to do it by opening a file in read-write mode (e.g. reading the entire file into memory, seeking and truncateing the file, and writing it back out), but I have to say I personally have almost never done this because it's not safe - if the program crashes, gets killed, the machine loses power, etc., you'll lose data! While not "inline" in the strictest sense, since rename is an atomic operation on many* filesystems, it's much safer - the file will either be the original or the updated version.
Example Code: Here is a demo of the rename method, and here is a demo of a trick using $^I, which is the same as Perl's -i switch (perlrun).
Lastly, I would strongly recommend Text::CSV for CSV file operations (also install Text::CSV_XS for speed).
| [reply] [d/l] [select] |
|
use POSIX;
use List::Util qw[min max];
use English;
use Getopt::Long;
use File::Basename;
use autodie;
my ($help,$csv_add, $csv_flex);
GetOptions (
"csv_add=s" => \$csv_add,
"csv_flex=s" => \$csv_flex,
"help" => \$help
);
if ($help || !$csv_add || !$csv_flex )
{
die "
Description: Provide the csv_add and csv_flex
Usage $0
\t-csv_add <path> -csv_flex <path> : full csv file paths
";
}
print "$csv_add\n";
print "$csv_flex\n";
open (CSVADD_FILE,"$csv_add") or die "Could not open $csv_add in read
+mode OR there is no file with name $csv_add";
open (CSVFLEX_FILE,"$csv_flex") or die "Could not open $csv_flex in re
+ad mode OR there is no file with name $csv_flex";
#removing the spaces between the fields of csv
open(STDOUT, "+>csv_add_up");
rm_spaces(\*CSVADD_FILE);
close STDOUT;
open(STDOUT, "+>csv_flex_up");
rm_spaces(\*CSVFLEX_FILE);
close STDOUT;
#chomp removes any new line character at the end of line and can retur
+n number of elements removed
#remove spaces and /I/0 and /T/0
sub rm_spaces {
my $file = shift;
while (<$file>) {
chomp;
my @fields = split /,/;
s/\s+//g for @fields;
s/\/I\/0//g for @fields;
s/\/T\/0//g for @fields;
print join ",", @fields,"\n";
}
}
Please suggest on how to make a single function that does the work of rm_spaces() and takes different files.
| [reply] [d/l] |
|
#!/usr/bin/perl
use strict;
my $csv_add = 'add.csv';
my $csv_flex = 'flex.csv';
print "
csv_add : $csv_add
csv_flex : $csv_flex\n";
for my $filename ($csv_add,$csv_flex){
if (-e $filename){
rm_spaces($filename);
} else {
warn "$filename not found";
}
}
# remove spaces,tabs,newlines,formfeeds,cr
# and /I/0 and /T/0
sub rm_spaces {
my $filename = shift;
my $bakfile = $filename.'.bak';
rename ($filename,$bakfile)
or die "Could not rename $filename to $bakfile";
open my $fh_in, '<',$bakfile or die "Could not open $bakfile";
open my $fh_out,'>',$filename or die "Could not open $filename";
while (<$fh_in>) {
s/\s+|\/[IT]\/0//g;
print $fh_out "$_\n";
}
close $fh_in;
close $fh_out;
unlink $bakfile;
}
poj | [reply] [d/l] |
|
Read perlintro, pass two(or 11teen) filenames as arguments to the function
| [reply] |
|
|