Perl regex to change date format - regex

I'm totally new to Perl, and I need to get a small find and replace done to change the date format in a set of large file.
The files have dates in the format: dd.mm.yyyy
and I need to change them to: mm-dd-yyyy
How do I do this with perl?
I have a basic code to read through the files in a directory and write to an output file, I'll need to have my replace logic in-between the while loop (if i'm not wrong!).
#!c:/perl64/bin/perl.exe
#loop around a directory
#files = <C:/perl64/data/*>;
# loop around files
foreach $file (#files) {
#Read File
open READ, $file or die "Cannot open $read for read :$!";
#Output File
$fname=substr($file, rindex($file,"/")+1,length($file)-rindex($file,"/")-1);
$write="C:/perl64/output/$fname";
open WRITE, ">$write" or die "Cannot open $write for write :$!";
#Loop Around file
while (<READ>) {
# TO DO: Change date format from dd.mm.yyyy to mm-dd-yyyy
#Write to ourput file
print WRITE "$_";
}
}
Regards,
Anand

You can use the substitution operator s///:
while (<READ>) {
s/(\d{2})\.(\d{2})\.(\d{4})/$2-$1-$3/;
print WRITE "$_";
}

Here's a simple script that will take optional arguments for input and output directories.
The use of opendir instead of a glob will save you some trouble cleaning up the file names.
use strict;
use warnings;
use autodie;
my $indir = shift || "C:/perl64/data";
my $outdir = shift || "C:/perl64/output";
opendir(my $in, $indir);
while (readdir $in) {
next unless -f;
open my $infile, '<', $_;
open my $outfile, '>', $outdir . "/" . $_;
while (<$infile>) {
s/([0-9]{2})\.([0-9]{2})\.([0-9]{4})/$2-$1-$3/g;
print $outfile $_;
}
}

Related

Using perl to process list of name and details

There are two files. one file is list of names. another file is list of names and details. I want to create 3rd file which contains names from 1st file and details(of that name) from 2nd file. Can you please suggest.
Details from 2nd file are delimited by pattern "list[i]"(which are names from 1st file) and "</reg>"
#!/usr/intel/bin/perl
use warnings;
use strict;
use Data::Dumper;
my $handle;
unless (open $handle, "<:encoding(utf8)", "/nfs/fm/disks/fm_nvm_7138/WLRD_LOGIC_users/cgoudarx/willard_b02/chiplevel/verif/testsuites/upf/pss_ret_regs.txt") {
print STDERR "Could not open file '/nfs/fm/disks/fm_nvm_7138/WLRD_LOGIC_users/cgoudarx/willard_b02/chiplevel/verif/testsuites/upf/pss_ret_regs.txt': $!\n";
# we return 'undefined', we could also 'die' or 'croak'
return undef
}
chomp(my #list = <$handle>);
unless (close $handle) {
# what does it mean if close yields an error and you are just reading?
print STDERR "Don't care error while closing '/nfs/fm/disks/fm_nvm_7138/WLRD_LOGIC_users/cgoudarx/willard_b02/chiplevel/verif/testsuites/upf/pss_ret_regs.txt': $!\n";
}
open ( INPUT, "/nfs/fm/disks/fm_nvm_7138/WLRD_LOGIC_users/cgoudarx/willard_b02/chiplevel/verif/testsuites/upf/tet.xml" ) or die("Could not open xml file.");
my $outffile ="newlist.xml";
open(FILEOUT2, ">$outffile") || die "ERROR: Can't open the output file $outffile: $!";
my $size = #list;
for (my $i=0; $i < $size; $i++) {
while( my $line = <INPUT> )
{
if ( $line =~ m/$list[$i]/) {
print FILEOUT2 $line;
while( $line = <INPUT>) # print till empty line
{
last if ( $line =~ m/<\/reg>/);
print FILEOUT2 $line;
}
print FILEOUT2 $line;
};
};
};
close(INPUT);
One of your input files is an XML document. You shouldn't parse XML documents with regular expressions. It is a far better idea to use a proper XML parser (I'd recommend XML::LibXML).
If you insist in parsing XML using regexes, then you cannot process your input file a line at a time, as XML elements will often (usually?) span multiple lines.
Also, please update your file-handling code to use the three-arg version of open() and lexical filehandles.
open ( my $in_fh, '<', "...") or die("Could not open xml file.");
And
open( my $out_fh, '>', $outffile) || die "ERROR: Can't open the output file $outffile: $!";
Oh, and it's a good idea to standardise on using or or || in those commands.

read a line from file and if pattern match then delete it from it in perl

I have a file place.txt with the content say:
I want to go Rome. Will Go.
I want to go Rome. Will Not Go.
I want to go Rome. Will Not Go.
I want to go Rome. Will Go.
I want to go India. Will Not Go.
I want to go India. Will Not Go.
I want to go Rome. Will Go.
I want to read this file and match the lines with the pattern "I want to go Rome." and omit those lines matching the pattern from this file in perl.
My sample code is:
$file = new IO::File;
$file->open("<jobs.txt") or die "Cannot open jobs.txt";
while(my $line = $file->getline){
next if $line =~ m{/I want to go Rome/};
print $line;
}
close $file;
Note: My file would be a big one. Can we use sed or awk?
It's as simple as
perl -ine'print unless /I want to go Rome/'
If you prefer script
use strict;
use warnings;
use autodie;
use constant FILENAME => 'jobs.txt';
open my $in, '<', FILENAME;
while (<$in>) {
if ( $. == 1 ) { # you need to do this after read first line
# $in will keep i-node with the original file
open my $out, '>', FILENAME;
select $out;
}
print unless /I want to go Rome/;
}
grep -v 'I want to go Rome' jobs.txt
Is much simpler still to write.
awk '$0~/I want to go Rome/' jobs.txt
try this :
use strict;
use warnings;
use File::Copy;
open my $file, "<", "jobs.txt" or die "Cannot open jobs.txt : $!";
open my $outtemp, ">", "out.temp" or die "Unable to open file : $!\n"; # create a temporary file to write the lines which doesn't match pattern.
while(my $line = <$file>)
{
next if $line =~ m/I want to go Rome/; # ignore lines which matches pattern
print $outtemp $line; # write the rest lines in temp file.
}
close $file;
close $outtemp;
move("out.temp", "jobs.txt"); # move temp file into original file.

No such file or directory error: Perl

I am naive in Perl. I have written the following code and I am breaking my head since two days because I am getting the following error when I am trying to open the file: No such file or directory at line 23 (open (FILE, "$config_file") or die $!;)
What I am doing is:
Open the folder and list all the files inside it.
Iterate over each files to look for a particular strings.
create new files for all of the files with the matching string replaced by some other string.
I would really appreciate your help.
Following is my code:
#!/usr/bin/perl -w
#~ The perl script that changes the IP addresses in configuration files from 192.168.3.x into 192.168.31.x in any particular folder
use strict;
use warnings;
use diagnostics;
#~ Get list of files in the Firewall folder
my $directory = 'C:\Users\asura\Desktop\ConfigFiles\Firewall';
opendir (my $dir, $directory) or die $!;
my #list_of_files = readdir($dir);
my $file;
while ($file = readdir ($dir)) {
push #list_of_files, $file;
}
closedir $dir;
print "#list_of_files\n";
#~ Iterate over each files to replace some strings
foreach my $config_file (#list_of_files) {
next unless (($config_file !~ /^\.+$/));
open (FILE, "$config_file") or die $!;
my #original_array = <FILE>;
close(FILE);
my #new_array;
foreach my $line (#original_array) {
chomp($line);
$line =~ s/192\.168\.3/192\.168\.31/g;
push (#new_array, $line);
}
print #new_array;
#~ Create a new files with modified strings
my $new_config_file = $config_file.1;
my $newfile = 'C:\Users\asura\Desktop\ConfigFiles\Firewall\$new_config_file';
open (NEW_FILE, ">", "$newfile") or die $!;
foreach (#new_array){
print NEW_FILE "$_\n";
}
close(NEW_FILE);
}
exit 0;
When you push items onto #list_of_files, you are pushing only the filename (the value returned from readdir). Unless your script is running in C:\Users\asura\Desktop\ConfigFiles\Firewall, the open at line 22 using just the filename (a relative path) will fail.
You need to push absolute paths onto #list_of_files at line 14, like so:
push #list_of_files, $directory . "\\" . $file;
Also, as #Michael-sqlbot mentions, you need to double-quote the string at line 35 for string interpolation to be performed (or use concatenation).
Finally, you should also properly quote the string concatenation on line 34.
The following is a simplification of your code that removes the bugs.
First off kudos including use strict and use warnings in EVERY script. One additional tool that you can use is use autodie; anytime that you're doing file processing.
The primary flaw in your code was the fact that you weren't including the path information when opening your files. There are two main ways to solve this. You can manually specify the path, like you did for your open to your output file handle, or you can use glob instead of opendir as that will automatically include the path in the returned results.
There was a secondary bug in your regex where you were missing a word boundary after .3. This would have led numbers in the thirties to matching mistakenly.
To simplify your code I just removed all of the superfluous temporary variables and instead process things file by file and line by line. This has the benefit of making it more clear when an input and output file handles are obviously related. Finally, if you're actually wanting to edit the files, there are lots of methods demonstrated at perlfaq4.
#!/usr/bin/perl -w
#~ The perl script that changes the IP addresses in configuration files from 192.168.3.x into 192.168.31.x in any particular folder
use strict;
use warnings;
use autodie;
use diagnostics;
#~ Get list of files in the Firewall folder
my $dir = 'C:\Users\asura\Desktop\ConfigFiles\Firewall';
opendir my $dh, $dir;
#~ Iterate over each files to replace some strings
while (my $file = readdir($dh)) {
next if $file =~ /^\.+$/;
open my $infh, '<', "$dir\\$file";
open my $outfh, '>', "$dir\\${file}.1"; #~ Create a new files with modified strings
while (<$infh>) {
s/(?<=192\.168)\.3\b/.31/g;
print $outfh $_;
}
close $infh;
close $outfh;
}
closedir $dh;

Replacing mutiple strings recursively within all files in a directory using Perl

I'm new with perl. saw many samples but had problems composing a solution
I have a list of strings which each string should be replaced in a different string a->a2, b->b34, etc. list of replacement is in some csv file. need to perform this replacement recursively on all files in directory.
might be any other language just thought perl would be the quickest
Your problem can be split into three steps:
Getting the search-and-replace strings from the CSV file,
Getting a list of all text files inside a given directory incl. subdirectories, and
Replacing all occurences of the search strings with their replacements.
So lets do a countdown and see how we can do that :)
#!/usr/bin/perl
use strict; use warnings;
3. Search and replace
We will define a sub searchAndReplace. It takes a file name as argument and accesses an outside hash. We will call this hash %replacements. Each key is a string we want to replace, and the value is the replacement. This "imposes" the restriction that there can only be one replacement per search string, but that should seem natural. I will further assume that each file is reasonably small (i.e. fits into RAM).
sub searchAndReplace {
my ($filename) = #_;
my $content = do {
open my $file, "<", $filename or die "Cant open $filename: $!";
local $/ = undef; # set slurp mode
<$file>;
};
while(my ($string, $replacement) = each %replacements) {
$content =~ s/\Q$string\E/$replacement/g;
}
open my $file, ">", $filename or die "Can't open $filename: $!";
print $file $content; # I didn't forget the comma
close $file;
}
This code is pretty straightforward, I escape the $string inside the regex so that the contents aren't treated as a pattern. This implementation has the side effect of possibly replacing part of the $content string where something already was replaced, but one could work around that if this is absolutely neccessary.
2. Traversing the file tree
We will define a sub called anakinFileWalker. It takes a filename or a name of an directory and the searchAndReplace sub as arguments. If the filename argument is a plain file, it does the searchAndReplace, if it is a directory, it opens the directory and calls itself on each entry.
sub anakinFileWalker {
my ($filename, $action) = #_;
if (-d $filename) {
opendir my $dir, $filename or die "Can't open $filename: $!";
while (defined(my $entry = readdir $dir)) {
next if $entry eq '.' or $entry eq '..';
# come to the dark side of recursion
anakinFileWalker("$filename/$entry", $action); # be sure to give full path
}
} else {
# Houston, we have a plain file:
$action->($filename);
}
}
Of course, this sub blows up if you have looping symlinks.
1. Setting up the %replacements
There is a nice module Text::CSV which will help you with all your needs. Just make sure that the %replacements meet the definition above, but that isn't hard.
Starting it all
When the %replacements are ready, we just do
anakinFileWalker($topDirectory, \&searchAndReplace);
and it should work. If not, this should have given you an idea about how to solve such a problem.

Why is this substitution involving the end of each line adding more to the beginning of the line?

I have some document into which I want to add something at the beginning and at the end of each line. The original document looks like this:
firstLine
secondline
I want to turn it into this:
put 'firstLine';
put 'secondline';
By using the following Perl script, I can only turn it into this:
put 'firstLine';
';put 'secondline';
It seems that there is a $ at the end of the first line and at the beginning of the second line. Could someone help me to figure out what is wrong with the following Perl script?
use File::Find;
use strict;
my ($filename, #lines, $oldterm, $newterm); #,$File::Find::name);
my $dir = ".";
open MYFILE, ">error.txt" or die $!;
find(\&edits, $dir);
sub edits() {
$filename = $File::Find::name;
if (grep(/\.txt$/, $filename)) { #only process the perl files
# open the file and read data
# die with grace if it fails
open(FILE, "<$filename") or die "Can't open $filename: $!\n";
#lines = <FILE>;
close FILE;
# open same file for writing, reusing STDOUT
open(STDOUT, ">$filename") or die "Can't open $filename: $!\n";
# walk through lines, putting into $_, and substitute 2nd away
for (#lines) {
s/(&.+)/' "$1" '/ig;
s/^/put '/ig;
s/$/';/ig;
print;
}
#Finish up
close STDOUT;
}
}
don't use regular expressions at all: you already have the lines separated in the #lines array:
for ( #lines ) {
chomp; # remove newline at the end of the implicit variable $_
print "puts '$_'\n";
}
If you do it in one step you should have better luck. Something like:
s/^(&.+)$/put '$1';/im;