how to change the contents in the file in perl? - 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

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"

Add a string at the beginning of the file with OPEN CLOSE function in Perl

My code did not work for my any modifications. other than append nothing works for in open close functions.
#!/usr/local/bin/perl
my $file = 'test';
open(INFO, $file);
print INFO "Add this line please\n";
print INFO "First line\n";
close(INFO);
You need to tell perl what type of filehandle you want
open(INFO, ">", "$file")|| die "Cannot open $file";
This will create and write to a file.
Look up
http://perldoc.perl.org/functions/open.html
By default open(INFO, $file) will take the file handle in read mode('<') . So until unless you specify the write mode('>') you cannot print the values into the file . When you write the code you should use : use strict; and use warnings; which will be helpful.
Code:
use strict;
use warnings;
my $InputFile = $ARGV[0];
open(FH,'<',"$InputFile")or die "Couldn't open the file $InputFile: $!";
my #file_content = <FH>;
close(FH);
open(FH,'>',"$InputFile") or die "Cannot open $InputFile: $!";
#String to be added at the begining of the file
my $file = "test";
print FH $file . "\n";
print FH #file_content;
close(FH);

Read Increment Then Write to a text file in perl

I have this little perl script which opens a txt file, reads the number in it, then overwrites the file with the number incremented by 1. I can open and read from the file, I can write to the file but I"m having issues overwriting. In addition, I'm wondering if there is a way to do this without opening the file twice. Here's my code:
#!/usr/bin/perl
open (FILE, "<", "data.txt") or die "$! error trying to a\
ppend";
undef $/;
$number = <FILE>;
$number = int($number);
$myNumber = $number++;
print $myNumber+'\n';
close(FILE);
open(FILE, ">data.txt") or die "$! error";
print FILE $myNumber;
close(FILE);
Change the line
$myNumber = $number++;
to
$myNumber = $number+1;
That should solve the problem.
Below is how you could do by opening the file just once:
open(FILE, "+<data.txt") or die "$! error";
undef $/;
$number = <FILE>;
$number = int($number);
$myNumber = $number+1;
seek(FILE, 0, 0);
truncate(FILE, tell FILE);
print $myNumber+"\n";
print FILE $myNumber;
close(FILE);
It's good that you used the three-argument form of open the first time. You also needed to do that in your second open. Also, you should use lexical variables, i.e., those which begin with my, in your script--even for your file handles.
You can just increment the variable that holds the number, instead of passing it to a new variable. Also, it's a good idea to use chomp. This things being said, consider the following option:
#!/usr/bin/env perl
use strict;
use warnings;
undef $/;
open my $fhIN, "<", "data.txt" or die "Error trying to open for reading: $!";
chomp( my $number = <$fhIN> );
close $fhIN;
$number++;
open my $fhOUT, ">", "data.txt" or die "Error trying to open for writing: $!";
print $fhOUT $number;
close $fhOUT;
Another option is to use the Module File::Slurp, letting it handle all the I/O operations:
#!/usr/bin/env perl
use strict;
use warnings;
use File::Slurp qw/edit_file/;
edit_file { chomp; $_++ } 'data.txt';
Try this:
#!/usr/bin/perl
use strict;
use warnings;
my $file = "data.txt";
my $number = 0;
my $fh;
if( -e $file ) {
open $fh, "+<", $file or die "Opening '$file' failed, because $!\n";
$number = <$fh>;
seek( $fh, 0, 0 );
} else { # if no data.txt exists - yet
open $fh, ">", $file or die "Creating '$file' failed, because $!\n";
}
$number++;
print "$number\n";
print $fh $number;
close( $fh );
If you're using a bash shell, and you save the code to test.pl, you can test it with:
for i in {1..10}; do ./test.pl; done
Then 'cat data.txt', should show a 10.

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.