printing the output of a derefrenced variable into a file - perl

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.

Related

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`

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"

How to catch errors with IO::File 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: $!");
}

Read and write file bit by bit

There is a .jpg file for example or some other file. I want to read it bit by bit. I do this:
open(FH, "<", "red.jpg") or die "Error: $!\n";
my $str;
while(<FH>) {
$str .= unpack('B*', $_);
}
close FH;
Well it gives me $str with 0101001 of the file. After that I do this:
open(AB, ">", "new.jpg") or die "Error: $!\n";
binmode(AB);
print AB $str;
close AB;
but it doesn't work.
How can I do it? and how to do that that it would work regardless of byte order(cross-platform)?
Problems:
You're didn't use binmode when reading too.
It makes no sense to read a binary file line by line since they don't have lines.
You're needlessly using global variables for your file handles.
And the one that answers your question: You didn't reverse the unpack.
open(my $FH, "<", "red.jpg")
or die("Can't open red.jpg: $!\n");
binmode($FH);
my $file; { local $/; $file = <$FH>; }
my $binary = unpack('B*', $file);
open(my $FH, ">", "new.jpg")
or die("Can't create new.jpg: $!\n");
binmode($FH);
print $FH pack('B*', $binary);

perl: Writing file at Nth position

I am trying to write in to file at Nth POSITION. I have tried with below example but it writes at the end. Please help to achieve this.
#!/usr/bin/perl
open(FILE,"+>>try.txt")
or
die ("Cant open file try.txt");
$POS=5;
seek(FILE,$POS,0);
print FILE "CP1";
You are opening the file in read-write appending mode. Try opening the file in read-write mode:
my $file = "try.txt";
open my $fh, "+<", $file
or die "could not open $file: $!";
Also, note the use of the three argument open, the lexical filehandle, and $!.
#!/usr/bin/perl
use strict;
use warnings;
#create an in-memory file
my $fakefile = "1234567890\n";
open my $fh, "+<", \$fakefile
or die "Cant open file: $!";
my $offset = 5;
seek $fh, $offset, 0
or die "could not seek: $!";
print $fh "CP1";
print $fakefile;
The code above prints:
12345CP190
If I understand you correctly, if the file contents are
123456789
you want to change that to
1234CP157689
You cannot achieve that using modes supplied to open (regardless of programming language).
You need to open the source file and another temporary file (see File::Temp. Read up to the insertion point from the source and write the contents to the temporary file, write what you want to insert, then write the remainder of the source file to the temporary file, close the source and rename the temporary to the source.
If you are going to do this using seek, both files must be opened in binary mode.
Here is an example using line oriented input and text mode:
#!/usr/bin/perl
use strict; use warnings;
use File::Temp qw( :POSIX );
my $source = 'test.test';
my $temp = tmpnam;
open my $source_h, '<', $source
or die "Failed to open '$source': $!";
open my $temp_h, '>', $temp
or die "Failed to open '$temp' for writing: $!";
while ( my $line = <$source_h> ) {
if ( $line =~ /^[0-9]+$/ ) {
$line = substr($line, 0, 5) . "CP1" . substr($line, 5);
}
print $temp_h $line;
}
close $temp_h
or die "Failed to close '$temp': $!";
close $source_h
or die "Failed to close '$source': $!";
rename $temp => $source
or die "Failed to rename '$temp' to '$source': $!";
this works for me
use strict;
use warnings;
open( my $fh, '+<', 'foo.txt' ) or die $!;
seek( $fh, 3, 0 );
print $fh "WH00t?";
this is also a more "modern" use of open(), see http://perldoc.perl.org/functions/open.html
The file will be closed when $fh goes out of scope ..
"Inserting" a string into a function can (mostly) be done in place. See the lightly used truncate built-in function.
open my $fh, '+<', $file or die $!;
seek $fh, 5, 0;
$/ = undef;
$x = <$fh>; # read everything after the 5th byte into $x
truncate $fh, 5;
print $fh "CPI";
print $fh $x;
close $fh;
If your file is line or record oriented, you can insert lines or modify individual lines easily with the core module Tie::File This will allow the file to be treated as an array and Perl string and array manipulation to be used to modify the file in memory. You can safely operate on huge files larger than your RAM with this method.
Here is an example:
use strict; use warnings;
use Tie::File;
#create the default .txt file:
open (my $out, '>', "nums.txt") or die $!;
while(<DATA>) { print $out "$_"; }
close $out or die $!;
tie my #data, 'Tie::File', "nums.txt" or die $!;
my $offset=5;
my $insert="INSERTED";
#insert in a string:
$data[0]=substr($data[0],0,$offset).$insert.substr($data[0],$offset)
if (length($data[0])>$offset);
#insert a new array element that becomes a new file line:
splice #data,$offset,0,join(':',split(//,$insert));
#insert vertically:
$data[$_]=substr($data[$_],0,$offset) .
substr(lc $insert,$_,1) .
substr($data[$_],$offset) for (0..length($insert));
untie #data; #close the file too...
__DATA__
123456789
234567891
345678912
456789123
567891234
678912345
789123456
891234567
912345678
Output:
12345iINSERTED6789
23456n7891
34567s8912
45678e9123
56789r1234
I:N:St:E:R:T:E:D
67891e2345
78912d3456
891234567
912345678
The file modifications with Tie::File are made in place and as the array is modified. You could use Tie::File just on the first line of you file to modify and insert as you requested. You can put sleep between the array mods and use tail -n +0 -f on the file and watch the file change if you wish...
Alternatively, if your file is reasonable size and you want to treat it like characters, you can read the entire file into memory, do string operations on the data, then write the modified data back out. Consider:
use strict; use warnings;
#creat the default .txt file:
open (my $out, '>', "nums.txt") or die $!;
while(<DATA>) { print $out "$_"; }
close $out or die $!;
my $data;
open (my $in, '<', "nums.txt") or die $!;
{ local $/=undef; $data=<$in>; }
close $in or die $!;
my $offset=5;
my $insert="INSERTED";
open (my $out, '>', "nums.txt") or die $!;
print $out substr($data,0,$offset).$insert.substr($data,$offset);
close $out or die $!;
__DATA__
123456789
2
3
4
5
6
7
8
9
Output:
12345INSERTED6789
2
3
4
5
6
7
8
9
If you treat files as characters, beware that under Windows, files in text mode have a \r\n for a new line. That is two characters if opened in binary mode.