How to catch errors with IO::File perl - perl

Normally you'd do:
open( my $fh, "+<", "$thefile.txt") or die "Could not open $thefile.txt $!\n";
but with IO::File you do (from docs):
$fh = new IO::File;
if ($fh->open("< file")) {
print <$fh>;
$fh->close;
}
Does IO::File automatically throw errors/die if there was a problem opening the file? How would one go about it when using this module?
Mostly concerned with logging, how would you log out a 'good' error message like 'No such file or directory'

From the docs for IO::File:
CONSTRUCTOR
new ( FILENAME [,MODE [,PERMS]] )
Creates an IO::File. If it receives any parameters, they are passed to the method open; if the open fails, the object is destroyed. Otherwise, it is returned to the caller.
Therefore, you can use or die statements just like you normally do:
use IO::File;
my $fh = IO::File->new('notfound.txt') or die "Can't open: $!";
Outputs:
Can't open: No such file or directory at script.pl line 5.

open( my $fh, "+<", "thefile.txt") or die "Could not open thefile.txt $!\n";
Remove $ sign from thefile.txt. You are not storing your file name in a variable.
you can use
$file = 'thefile.txt';
open( my $fh, "<", "$file") or die "Could not open $file $!\n"
In die the "$!" would show the error message as "No such file or directory"

$fh->open("< file") will be false if it cannot open the file. So just add an else:
$fh = new IO::File;
if ($fh->open("< file")) {
print <$fh>;
$fh->close;
}
else {
die("Cannot open file. Reason: $!");
}

Related

how to change the contents in the file in perl?

I am trying to open one file read oneline in it at a time then open the another file and try to search for some part of the line read from the first file in the second file and try to replace all instances with the other part of the line read from the first file.When i am executing it its getting executed and i am able to see the result on the console but the files are not getting modified. What could be the mistake. Can some one please suggest this.
use strict;
use warnings;
use autodie; # die if problem reading or writing a file
my $filename = 'compare.txt';
open(my $fh, '+<', $filename) or die "Could not open file '$filename' $!";
while(<$fh>){
my $readline= "$_";
print("\n");
my #arr=split(',',$readline);
print($arr[0]."\n".$arr[1]);
replace($arr[0],$arr[1]);
}
close $fh;
sub replace
{
my $search=shift(#_);
my $replace=shift(#_);
my $filename2 = 'replace.txt';
open(my $fh1, '+<', $filename2) or die "Could not open file '$filename' $!";
while(<$fh1>)
{
my $readline2= "$_";
$readline2=~s/$search/$replace/g;
print($readline2);
print("\n");
}
close $fh1;
}
As a huge fan of the Path::Tiny module, I would do the above as:
use 5.014;
use warnings;
use Path::Tiny;
my %rep = map { split /,/ } path('compare.txt')->lines({chomp => 1});
path("replace.txt")->edit_lines( sub {
while(my($key,$val) = each(%rep)) {
s/$key/$val/g;
}
});
In your sub, when you are iterating the lines in your file, you should write it back to a file. The regex substitute doesn't automatically write it back to file.
use strict;
use warnings;
use autodie; # die if problem reading or writing a file
use File::Copy;
my $filename = 'compare.txt';
open(my $fh, '+<', $filename) or die "Could not open file '$filename' $!";
while(<$fh>){
my $readline= "$_";
print("\n");
my #arr=split(',',$readline);
print($arr[0]."\n".$arr[1]);
replace($arr[0],$arr[1]);
}
close $fh;
sub replace
{
my $search=shift(#_);
my $replace=shift(#_);
my $filename2 = 'replace.txt';
open(my $fh1, '+<', $filename2) or die "Could not open file '$filename' $!";
#open file to write to
open $newfile, '>', 'replace_tmp.txt';
while(<$fh1>)
{
chomp;
my $readline2= "$_";
$readline2=~s/$search/$replace/g;
print( $newfile, $readline2);
print($newfile, "\n");
}
close($fh1);
close($newfile);
move ('replaced.txt', 'replace.txt');
}
This is simple way of doing it. You can use File::Tie to write back to the same file and avoid renaming it, or refer to perldoc

Perl-Copying file from one location to other but content not copying

I am writing a script in perl where I am creating a file and getting input from user for file but when I am copying that file to other location the file is copying but it is empty only. My code is
# !/usr/bin/perl -w
for($i = 1;$i<5;$i++)
{
open(file1,"</u/man/fr$i.txt");
print "Enter text for file $i";
$txt = <STDIN>;
print file1 $txt;
open(file2,">/u/man/result/fr$i.txt");
while(<file1>)
{
print file2 $_;
}
close(file1);
close(file2);
}
fr1 to fr4 are creating but these are empty. like when I run my code it is asking for input i provide the input and code run without error but still the files are empty. Please help.
in line number 4 I changed < to > also as I thought for creating new file it might need that but still it is not working
You need to close the filehandle that was written to in order to be able to read from that file.
use warnings;
use strict;
use feature 'say';
for my $i (1..4)
{
my $file = "file_$i.txt";
open my $fh, '>', $file or die "Can't open $file: $!";
say $fh "Written to $file";
# Opening the same filehandle first *closes* it if already open
open $fh, '<', $file or die "Can't open $file: $!";
my $copy = "copy_$i.txt";
open my $fh_cp, '>', $copy or die "Can't open $copy: $!";
while (<$fh>) {
print $fh_cp $_;
}
close $fh_cp; # in case of early errors in later iterations
close $fh;
}
This creates the four files, file_1.txt etc, and their copies, copy_1.txt etc.
Please note the compulsory checking whether open worked.
You can't write to a filehandle that's not open for writing. You can't read from a filehandle that's not open for reading. Never ignore the return value of open.
# !/usr/bin/perl
use warnings; # Be warned about mistakes.
use strict; # Prohibit stupid things.
for my $i (1 .. 4) { # lexical variable, range
open my $FH1, '>', "/u/man/fr$i.txt" # 3 argument open, lexical filehandle, open for writing
or die "$i: $!"; # Checking the return value of open
print "Enter text for file $i: ";
my $txt = <STDIN>;
print {$FH1} $txt;
open my $FH2, '<', "/u/man/fr$i.txt" # Reopen for reading.
or die "$i: $!";
open my $FH3, '>', "/u/man/result/fr$i.txt" or die "$i: $!";
while (<$FH2>) {
print {$FH3} $_;
}
close $FH3;
}
I opened the file in write mode using filehandler1 Then i again opened the file in read mode using same filehandler1 then I opened filehandler2 for destiantion So it is working fine for me then.
system("cp myfile1.txt /somedir/myfile2.txt")
`cp myfile1.txt /somedir/myfile2.txt`

Open file with or without extension

This is how I open a file in Perl:
open FILE, "</file.ext";
How do I open the file file regardless of if it's called file or file.ext in Perl?
You can use grep:
use warnings;
use strict;
use Errno qw( ENOENT );
my ($file) = grep { -f $_ } qw(file file.ext)
or die $!=ENOENT;
open my $fh, '<', $file
or die "$file: $!";
The following will produce the most usable error message:
sub open_if_exists {
my ($qfn) = #_;
my $fh;
open($fh, '<', $qfn)
and return $fh;
$!{ENOENT}
and return undef;
die("Can't open \"$qfn\": $!\n");
}
my $qfn = "file";
my $fh = open_if_exists($qfn) || open_if_exists("$qfn.ext")
or die("Can't open \"$qfn\" or \"$qfn.ext\": $!\n");
open returns 0 on failure, so you can chain open calls together with the || or or operators.
my $fh;
open ($fh, '<', 'file') ||
open ($fh, '<', 'file.ext') ||
open ($fh, '<', $other_default_filename) ||
die "Couldn't find any acceptable file: $!";
The typical approach is to pass the filename to the piece of the code that needs to open the file:
sub f {
my ($filename) = #_;
open my $fh, '<', $filename or die "$!";
# do things with $fh
close $fh;
return;
}
now it doesn't matter what the name of the file is, function f will open it (assuming it exists and you have permissions to read it):
f('file');
f('file.ext');
You could also pass the filename as a command-line argument:
#!perl
# test-open.pl
use strict;
use warnings;
my $filename = shift #ARGV or die "Missing filename";
open my $fh, '<', $filename or die "$!";
now you call test-open.pl, passing it a filename:
perl test-open.pl file
perl test-open.pl file.ext

Printing a content of a file to the screen in perl

I Have a perl script which write a few lines into file. (I checked and see that the file is written correctly)
right after that I want to print the content to the screen, the way I'm trying to do it- is to read the file and print it
open (FILE, '>', "tmpLogFile.txt") or die "could not open the log file\n";
$aaa = <FILE>;
close (FILE);
print $aaa;
but I get nothing on the screen, what do I do wrong?
To read you need to specify the open mode as <.
Also, $aaa = <FILE> has scalar context, and only reads a line.
Using print <FILE> you can have list context and read all lines:
open (FILE, '<', "tmpLogFile.txt") or die "could not open the log file\n";
print <FILE>;
close (FILE);
try this:
use strict;
use warnings;
my $filename = 'data.txt';
open(my $fh, '<:encoding(UTF-8)', $filename)
or die "Could not open file '$filename' $!";
while (my $row = <$fh>) {
chomp $row;
print "$row\n";
}
print "done\n"

printing the output of a derefrenced variable into a file

I had a written a module, just to bifurcate a file into training and test sets. The output is fine, but it would be really easy for the students if the output of the two referenced variables, #$test and #$training were redirected to two different files. Here is the code:
use Cut;
my($training,$test)=Cut::cut_80_20('data.csv') ;
print"======TRAINING======\n"."#$training\n";
print"======TEST==========\n"." #$test\n";
print takes an optional filehandle before the data to output. Open your files and print away:
open( my $training_fh, '>', 'training.csv' ) or die "Couldn't open training.csv: $!";
print $training_fh "======TRAINING======\n"."#$training\n";
open( my $test_fh, '>', 'test.csv' ) or die "Couldn't open test.csv: $!";
print $test_fh "======TEST==========\n"." #$test\n";
It's very easy:
open my $fh1, '>', "training.out" or die "failed to open training.out ($!)";
print $fh1 "======TRAINING======\n";
print $fh1 "#$training\n";
close $fh1;
open my $fh2, '>', "test.out" or die "failed to open test.out ($!)";
print $fh2 "======TEST==========\n";
print $fh2 "#$test\n";
close $fh2;
Note the absence of a comma after the file handle in the print statements. You can add newlines and such like as necessary.