Writing results in a text file with perl - perl

I have a problem when the script print the whole line of text file in a result text file:
use strict;
use warnings;
use autodie;
my $out = "result2.txt";
open my $outFile, ">$out" or die $!;
my %permitted = do {
open my $fh, '<', 'f1.txt';
map { /(.+?)\s+\(/, 1 } <$fh>;
};
open my $fh, '<', 'f2.txt';
while (<$fh>) {
my ($phrase) = /(.+?)\s+->/;
if ($permitted{$phrase}) {
print $outFile $fh;
}
close $outFile;
The problem is in this line
print $outFile $fh;
Any idea please?
Thank you

print $outFile $fh is printing the value of the file handle $fh to the file handle $outFile. Instead you want to print the entire current line, which is in $_.
There are a couple of other improvements that can be made
You should always use the three-parameter form of open, so the open mode appears on its own as the second paremeter
There is no need to test the success of an open of autodie is in place
If you have a variable that contains the name of the output file, then you really should have ones for the names of the two input files as well
This is how your program should look. I hope it helps.
use strict;
use warnings;
use autodie;
my ($in1, $in2, $out) = qw/ f1.txt f2.txt result2.txt /;
my %permitted = do {
open my $fh, '<', $in1;
map { /(.+?)\s+\(/, 1 } <$fh>;
};
open my $fh, '<', $in2;
open my $outfh, '>', $out;
while (<$fh>) {
my ($phrase) = /(.+?)\s+->/;
if ($permitted{$phrase}) {
print $outfh $_;
}
}
close $outfh;

I think you want print $outfile $phrase here, don't you? The line you currently have is trying to print out a file handle reference ($fh) to a file ($outfile).
Also, just as part of perl best practices, you'll want to use the three argument open for your first open line:
open my $outFile, ">", $out or die $!;
(FWIW, you're already using 3-arg open for your other two calls to open.)

Although Borodin has provided an excellent solution to your question, here's another option where you pass your 'in' files' names to the script on the command line, and let Perl handle the opening and closing of those files:
use strict;
use warnings;
my $file2 = pop;
my %permitted = map { /(.+?)\s+\(/, 1 } <>;
push #ARGV, $file2;
while (<>) {
my ($phrase) = /(.+?)\s+->/;
print if $permitted{$phrase};
}
Usage: perl script.pl inFile1 inFile2 [>outFile]
The last, optional parameter directs output to a file.
The pop command implicitly removes inFile2's name off of #ARGV, and stores it in $file2. Then, inFile1 is read using the <> directive. The file name of inFile2 is then pushed onto #ARGV, and that file is read and a line is printed if $permitted{$phrase} is true.
Running the script without the last, optional parameter will print results (if any) to the screen. Using the last parameter saves output to a file.
Hope this helps!

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`

How to replace ^M character in perl code in same file

I am looking to delete control-M character file in perl code (not perl one liner).
I tried this, but it will write to new file. Whats the way to do it in same file?
#!/usr/bin/perl
open (IN, '<', "FILE.dat") or die "$!";
open (OUT, '>', "FILE.dat.cpy") or die "$!";
while(<IN>)
{
$line = $_;
$line=~ tr/\015//d;
print OUT "$line";
}
close (IN);
close (OUT);
store the file internal in a String.
#!/usr/bin/perl
my $content = ''
open (IN, '<', "FILE.dat") or die "$!";
while(<IN>)
{
$line = $_;
$line=~ tr/\015//d;
$content .$line
}
close (IN);
open (OUT, '>', "FILE.dat") or die "$!";
print OUT $line;
close (OUT);
It could be done using $^I variable.
use autodie;
local $^I = "";
local #ARGV = "FILE.dat";
while (my $line = <>) {
$line=~ tr/\r//d;
print $line;
}
From perlvar
$^I - The current value of the inplace-edit extension. Use undef to disable inplace editing.
So it is undef by default, empty string is used to edit in-place, and non-empty string will be added as suffix to backup file name.
You needn't be afraid of coping the edited content to a new file, and then renaming. This is the standard way to edit content unless you're dealing with a file with fixed with records.
Check out How do I change, delete, or insert a line in a file, or append to the beginning of a file? to observe most of the ways to edit content, and the majority of them will ultimately be copying to a new file.
My preferred advice is to use in-place edit as demonstrated by mpapec. The only addition is that Windows forces you to specify a backup, so just need to add the unlink line after the processing.
use strict;
use warnings;
use autodie;
my $file = 'FILE.dat';
local #ARGV = $file;
local $^I = '.bak';
while (<>) {
tr/\r//d;
print;
}
unlink "$file$^I";
If you insist on being able to open the file only once, then perhaps you can take a look at I still don't get locking. I just want to increment the number in the file. How can I do this?
use strict;
use warnings;
use autodie;
use Fcntl qw(:seek);
my $file = 'afile.dat';
open my $fh, '+<:raw', $file;
my $data = do {local $/; <$fh>};
seek $fh, SEEK_SET, 0;
truncate $fh, 0;
$data =~ tr/\r//d;
print $fh $data;
close $fh;

redirection of the result in a file text

I do a perl scrip that it creates a hash directly from the contents of the first file, and then reads each line of the second, checks the hash to see if it should be printed.
Here is the perl script :
use strict;
use warnings;
use autodie;
my %permitted = do {
open my $fh, '<', 'f1.txt';
map { /(.+?)\s+\(/, 1 } <$fh>;
};
open my $fh, '<', 'f2.txt';
while (<$fh>) {
my ($phrase) = /(.+?)\s+->/;
print if $permitted{$phrase};
}
I am looking for how i print the result in a file text because this script actually print the result on the screen.
Thank you in advance.
Cordially
$ perl thescript.pl > result.txt
Will run your script and put the printed output in result.txt
Or, from within the script itself:
use strict;
use warnings;
use autodie;
my %permitted = do {
open my $fh, '<', 'f1.txt';
map { /(.+?)\s+\(/, 1 } <$fh>;
};
# Open result.txt for writing:
open my $out_fh, '>', 'result.txt' or die "open: $!";
open my $fh, '<', 'f2.txt';
while (<$fh>) {
my ($phrase) = /(.+?)\s+->/;
# print output to result.txt
print $out_fh $_ if $permitted{$phrase};
}
Open a new filehandle in write mode, then print to it. See perldoc -f print or http://perldoc.perl.org/functions/print.html for more info
...
open my $fh, '<', 'f2.txt';
open my $out_fh, '>', 'output.txt';
while (<$fh>) {
my ($phrase) = /(.+?)\s+->/;
print $out_fh $_
if $permitted{$phrase};
}
mapping the file contents first produces a list of all of the file's lines. This isn't necessarily a bad thing, unless the file's substantially large. grebneke showed how to direct output to a file, using > result.txt. Given this, and the (possible) map issue, consider just passing both files to the script from the command line, and process them using whiles:
use strict;
use warnings;
my %permitted;
while (<>) {
$permitted{$1} = 1 if /(.+?)\s+\(/;
last if eof;
}
while (<>) {
print if /(.+?)\s+->/ and $permitted{$1};
}
Usage: perl script.pl f1.txt f2.txt > result.txt
Hope this helps!

Read directory and save contents of files to new file in Perl

I want to save the contents of some files into a new file, and I do the following:
use strict;
use warnings;
use HTML::TreeBuilder::XPath;
my ($dir) = #ARGV;
my #files = glob "details/*";
my $filename = 'target.txt';
for my $file (#files) {
my $tree = HTML::TreeBuilder::XPath->new_from_file($file);
my #opacity = $tree->findnodes_as_strings('//div[#class="opacity description"]');
open my $fh, '>>', $filename;
print $fh for #opacity;
}
Unfortunately it will not work. And I dont understand why?
Check the return value of open:
open my $fh ">>", $filename or die "Can't open $filename: $!";
This can provide invaluable insights when “something isn't working”.
Your syntax for print is ambiguous. Use print or say like
print FILEHANDLE LIST
print {EXPR} LIST # EXPR has to produce a filehandle object
print LIST # prints to the `select`ed filehandle, usually STDOUT
print # prints $_ by default
So you want to explicitly specify what you are printing, and probably also add a newline after each element in #opacity. So either
print {$fh} "$_\n" for #opacity;
or use feature 'say' (perl 5.10 and better):
say {$fh} $_ for #opacity;

Perl problems printing output to a new file

I want to remove all lines in a text file that start with HPL_ I have acheived this and can print to screen, but when I try to write to a file, I just get the last line of the amended text printed in the new file. Any help please!
open(FILE,"<myfile.txt");
#LINES = <FILE>;
close(FILE);
open(FILE,">myfile.txt");
foreach $LINE (#LINES) {
#array = split(/\:/,$LINE);
my $file = "changed";
open OUTFILE, ">$file" or die "unable to open $file $!";
print OUTFILE $LINE unless ($array[0] eq "HPL_");
}
close(FILE);
close (OUTFILE);
exit;
You just want to remove all lines that start with HPL_? That's easy!
perl -pi -e 's/^HPL_.*//s' myfile.txt
Yes, it really is just a one-liner. :-)
If you don't want to use the one-liner, re-write the "write to file" portion as follows:
my $file = "changed";
open( my $outfh, '>', $file ) or die "Could not open file $file: $!\n";
foreach my $LINE (#LINES) {
my #array = split(/:/,$LINE);
next if $array[0] eq 'HPL_';
print $outfh $LINE;
}
close( $outfh );
Note how you are open()ing the file each time through the loop. This is causing the file to only contain the last line, as using open() with > means "overwrite what's in the file". That's the major problem with your code as it stands.
Edit: As an aside, you want to clean up your code. Use lexical filehandles as I've shown. Always add the three lines that tchrist posted at the top of every one of your Perl programs. Use the three-operator version of open(). Don't slurp the entire file into an array, as if you try to read a huge file it could cause your computer to run out of memory. Your program could be re-written as:
#!perl
use strict;
use autodie;
use warnings FATAL => "all";
my $infile = "myfile.txt";
my $outfile = "changed.txt";
open( my $infh, '<', $infile );
open( my $outfh, '>', $outfile );
while( my $line = <$infh> ) {
next if $line =~ /^HPL_/;
print $outfh $line;
}
close( $outfh );
close( $infh );
Note how with use autodie you don't need to add or die ... to the open() function, as the autodie pragma handles that for you.
The issue with your code is that you open the file for output within your line-processing loop which, due to your use of the '>' form of open, opens the file each time for write, obliterating any previous content.
Move the invocation of open() to the top of your file, above the loop, and it should work.
Also, I'm not sure of your intent but at line 4 of your example, you reopen your input file for write (using '>'), which also clobbers anything it contains.
As a side note, you might try reading up on Perl's grep() command which is designed to do exactly what you need, as in:
#!/usr/bin/perl
use strict;
use warnings;
open(my $in, '<', 'myfile.txt') or die "failed to open input for read: $!";
my #lines = <$in> or die 'no lines to read from input';
close($in);
# collect all lines that do not begin with HPL_ into #result
my #result = grep ! /^HPL_/, #lines;
open(my $out, '>', 'changed.txt') or die "failed to open output for write: $!";
print { $out } #result;
close($out);