separating a string which does not has a delimiter in it - perl

Can we separate a string using index values, so that my input which comprises of combined characters can be separated and the final output would be a string which is readable.
Input = 20140610182213
Expecting Output = 2014-06-10 18:22:13

I usually use a substitution for similar tasks:
my $input = '20140610182213';
$input =~ s/(....)(..)(..)(..)(..)(..)/$1-$2-$3 $4:$5:$6/;
print $input;
Another possibility is to use substr:
my $input = '20140610182213';
my #delims = ('-', '-', ' ', ':', ':');
substr $input, $_, 0, pop #delims for 12, 10, 8, 6, 4;
print $input;

You might like unpack for this:
my ( $year, $mon, $mday, $hour, $min, $sec ) = unpack "A4 A2 A2 A2 A2 A2", "20140610182213";
say "$year-$mon-$mday $hour:$min:$sec";

Let's get all modern on you:
use strict;
use warnings;
use feature qw(say);
my $input = "20140610182213";
$input =~ /(?<year>\d{4})
(?<month>\d{2})
(?<day>\d{2})
(?<hour>\d{2})
(?<minute>\d{2})
(?<second>\d{2})/x;
say "$+{year}-$+{month}-$+{day} $+{hour}:$+{minute}:$+{second}";
I'm using named back references here. In Perl since the very early beginning, you always had numeric back references which I could set by using parentheses:
$input =~ /(\d{4})(\d{2})(\d{2})/;
my $year = $1;
my $month = $2;
my $day = $3;
Each parentheses grouping was a back reference. This was taken directly from sed.
In Perl 5.10, named back references can now be used. They're in the format of (?<name>regex) where name is the name of the back reference and regex is the regular expression. To refer to them, you use $+{name} where name is your back reference name.
The big advantage is that you now have actual names for your back references, and you don't have to worry what $2 means:
$input =~ /(?<year>\d{4})(?<month>\d{2})(?<day>\d{2})/;
my $year = $+{year};
my $month = $+{month};
my $day = $+{day};
Now, we use the x flag in regular expressions. This allows us to have multiple line regular expressions:
$input =~ /(?<year>\d{4})
(?<month>\d{2})
(?<day>\d{2})/x; #The /x flag
my $year = $+{year};
my $month = $+{month};
my $day = $+{day};
If you're not familiar with the syntax, it can be a bit hard on the eyes at first. However, one of the nice things about this is that it documents what's going on, and makes maintenance easer. Once your eyes adjust to the light, it is easy to see what is going on and find errors.
Another possibility is to use Time::Piece to convert that date time to something that Perl can directly manipulate:
use strict;
use warnings;
use feature qw(say);
use Time::Piece;
my $input = "20140610182213";
my $date_object = Time::Piece->strptime($input, "%Y%m%d%H%M%S");
printf "%04d-%02d-%02d %02d:%02d:%02d\n",
$date_object->year,
$date_object->mon,
$date_object->mday,
$date_object->hour,
$date_object->minute,
$date_object->second;
Again, what's going on is well documented. You can see what the input string is, and you can easily refer to each part of the string, and even change the formatting. What if you want the name of the month? Use $date_object->month.
For simple parsing and never using this again, the first way is probably the best. However, by using Time::Piece, you can now check to see how days there are between two dates (for example, how old is your date/time stamp?).

I think this is tidiest using a global pattrn match with sprintf:
my $dt = sprintf '%s%s-%s-%s %s:%s:%s', '20140610182213' =~ /../g;
print $dt;
output
2014-06-10 18:22:13

If you want the values assigned to variables (e.g. $y, $m,$d etc.) for use later:
perl -E '($y,$m,$d,$H,$M,$S)="20140610182213"=~/(....)(..)(..)(..)(..)(..)/;say "$y-$m-$d $H:$M:$S"'

Just use Time::Piece
use strict;
use warnings;
use Time::Piece;
my $string = "20140610182213";
my $date = Time::Piece->strptime($string, "%Y%m%d%H%M%S");
print $date->strftime("%Y-%m-%d %H:%M:%S"), "\n";
Outputs:
2014-06-10 18:22:13

Related

How to extract the date from the string pattern in perl

How do I extract date Simple_Invoice_Report.Summary.20150701000000.csv from this string in perl?
i.e. 20150701
Can you please help me out.
The simplest solution is a regular expression match. Assuming your date is always the first 8 digits in your string:
my ( $date ) = ( $string =~ m/(\d{8})/ );
If it's more complicated than that, you'll need to be a bit more specific.
Below is the code to extract the data from the above string:
#!/usr/bin/perl
use strict;
use warnings;
my $string = q{Simple_Invoice_Report.Summary.20150701000000.csv};
if($string =~ m#.(\d{8})\d+.csv#g){
print $1;
}
Output: 20150701

Date::Manip::Delta - the number of seconds [duplicate]

The code below only expresses the difference in months and days like so:
0:2:0:5:0:0:0
So it works, but I want to know the total number of days given that $ADDate can vary quite a bit. Hopefully this is simple, and I just completely missed how to do it.
#!/usr/bin/perl
use Date::Manip 6.42;
my $ADDate = "20131211000820.0Z";
my $var;
my #val;
my $diff;
calc_period($ADDate = "20131211000820.0Z");
sub calc_period
{
$ADDate =~ s/^([\d][\d][\d][\d])([\d][\d])([\d][\d])/$1-$2-$3/gs;
$ADDate =~ s/.........$//gs;
$today = ParseDate("today");
$beginning = ParseDate($ADDate);
$end = ParseDate($today);
$delta = DateCalc($beginning,$end,\$err,1);
#$delta =~ s/([\d+][:][\d+]):.*$/$1/gs;
print "$delta\n";
print "$ADDate\n";
}
I'm not familiar with Date::Manip, but I think another way to do this is to use Time::Piece to parse your string and do whatever you like with that since taking the difference of two Time::Piece object returns a Time::Seconds object.
The following example will show the difference of the current time and the hardcoded time and show it in days.
#!/usr/bin/perl
use strict;
use warnings;
use Time::Piece;
use Time::Seconds;
my $d = "20131211000820.0Z";
my $t = Time::Piece->strptime($d, "%Y%m%d%H%M%S.0Z");
my $now = Time::Piece->localtime();
my $diff = Time::Seconds->new($now - $t);
print $diff->days, "\n";
NigoroJr has already given you an answer. However, just as an FYI, the following is how I would clean up the code you originally provided:
#!/usr/bin/perl
use Date::Manip 6.42;
use strict;
use warnings;
calc_period("20131211000820.0Z");
sub calc_period {
my $date = shift;
$date =~ s/^(\d{4})(\d{2})(\d{2}).*/$1-$2-$3/;
my $beginning = ParseDate($date);
my $end = ParseDate("today");
my $delta = DateCalc($beginning, $end, \my $err, 1);
#$delta =~ s/([\d+][:][\d+]):.*$/$1/gs;
print "$delta\n";
print "$date\n";
}
Biggest differences being the proper use of a function and scoped variables, and a simplification of your regex.
I was unable to find a clean way to get Date::Manip to output a strict delta in days though, so the other module is the way to go.

How to display 4/25/10 to 2010-25-04?

my$str= '4/25/10';
my$sr = join(' ',split (/\//,$str));
#my$s = sprintf '%3$d %2$d %1$d',$srt;
print$sr,"\n";
output:
4 25 10
But i want output like 2010-25-04.Can any one suggest me how display the my desire output.Give me your suggestion
you answers will be appreciable.
Well, a braindead solution might be:
my #date = split( /\//,$str)
printf("%04d-%02d-%02d", $date[2] + 2000, $date[1], $date[0]);
You could write something a little more self-documenting by highlighting what you expect to be year, month and day like so:
my ($day, $month, $year) = split /\//, $str;
printf("%04d-%02d-%02d", $year + 2000, $month, $day);
You're not that far off.
Instead of splitting and joining in a single operation, you can keep individual variables to handle the data better:
my ($d,$m,$y) = split /\//, $str;
Then you can format it in most any way you please, for example:
printf "20%02d-%02d-%02d\n", $y, $d, $m;
A few notes, though:
I won't comment about the source format, but the format you're converting to doesn't make a lot of sense. You'd probably be better using ISO-8601: 2010-04-25.
Obviously, this way of doing only works up to year 2099.
For anything more serious, you'd be better off delegating this kind of work to date handling modules. See for example this question for parsing and this question for formatting
use DateTime::Format::Strptime;
my $strp = DateTime::Format::Strptime->new(
pattern => '%m/%d/%y',
locale => 'en_US'
);
my $dt = $strp->parse_datetime('4/25/10');
print $dt->strftime('%Y-%d-%m'), "\n";
Gives:
2010-25-04
No need to do to DateTime for this. Time::Piece has been included with the Perl core distribution for years.
#!/usr/bin/perl
use strict;
use warnings;
use 5.010;
use Time::Piece;
my $date = '4/25/10';
my $date_tp = Time::Piece->strptime($date, '%m/%d/%y');
say $date_tp->strftime('%Y-%m-%d');

Today's Date in Perl in MM/DD/YYYY format

I'm working on a Perl program at work and stuck on (what I think is) a trivial problem. I simply need to build a string in the format '06/13/2012' (always 10 characters, so 0's for numbers less than 10).
Here's what I have so far:
use Time::localtime;
$tm=localtime;
my ($day,$month,$year)=($tm->mday,$tm->month,$tm->year);
You can do it fast, only using one POSIX function. If you have bunch of tasks with dates, see the module DateTime.
use POSIX qw(strftime);
my $date = strftime "%m/%d/%Y", localtime;
print $date;
You can use Time::Piece, which shouldn't need installing as it is a core module and has been distributed with Perl 5 since version 10.
use Time::Piece;
my $date = localtime->strftime('%m/%d/%Y');
print $date;
output
06/13/2012
Update
You may prefer to use the dmy method, which takes a single parameter which is the separator to be used between the fields of the result, and avoids having to specify a full date/time format
my $date = localtime->dmy('/');
This produces an identical result to that of my original solution
use DateTime qw();
DateTime->now->strftime('%m/%d/%Y')
expression returns 06/13/2012
If you like doing things the hard way:
my (undef,undef,undef,$mday,$mon,$year) = localtime;
$year = $year+1900;
$mon += 1;
if (length($mon) == 1) {$mon = "0$mon";}
if (length($mday) == 1) {$mday = "0$mday";}
my $today = "$mon/$mday/$year";
use Time::Piece;
...
my $t = localtime;
print $t->mdy("/");# 02/29/2000
Perl Code for Unix systems:
# Capture date from shell
my $current_date = `date +"%m/%d/%Y"`;
# Remove newline character
$current_date = substr($current_date,0,-1);
print $current_date, "\n";
Formating numbers with leading zero is done easily with "sprintf", a built-in function in perl (documentation with: perldoc perlfunc)
use strict;
use warnings;
use Date::Calc qw();
my ($y, $m, $d) = Date::Calc::Today();
my $ddmmyyyy = sprintf '%02d.%02d.%d', $d, $m, $y;
print $ddmmyyyy . "\n";
This gives you:
14.05.2014

Pointers in Perl

How to use pointer concepts in Perl? For example I have a line and want to search for the given string any where in the that line by positioning using the pointer. Kindly suggest me.
So I have 2 files with a key it like in 1st file I have data as in following columns with value in it as...
ID|Rating_Provider|Time|QualityRating z6Y1kWFT99|S&P_LONG|20110120 12:00:00 AM|NR z6Y1kWFT99|MOODY'S_LONG|20101101 12:00:00 AM|NR
and in 2nd file I have data as in following columns in it as...
ID|BBCMPSEC|QualityRating_S&P_LONG|Time_S&P_LONG|QualityRating_MOODY'S_LONG|Time_‌​MOODY'S_LONG
Now finally I need to see the data as...
ID|BBCMPSEC|QualityRating_S&P_LONG|Time_S&P_LONG|QualityRating_MOODY'S_LONG‌​|Time_MOODY'S_LONG z6Y1kWFT99|xxx|NR|20110120 12:00:00 AM.
ETA: Based on your comments, I would say that you'd be best off using Text::CSV. Take a look at the documentation, it is quite helpful. Basically, you would do something like:
use Text::CSV;
my $csv = Text::CSV->new({
binary => 1,
sep_char => "|",
});
open my $fh, "<", "inputfile" or die $!;
while (my $row = $csv->getline($fh)) {
# #$row now contains your row data
}
Old answer
"pointers" are not used in perl. I assume you mean the position of the match. There are several ways. You can use index if you do not need any regexes:
perl -lwe 'print index("foobar", "bar");'
If you do need a regex, perhaps for some more complicated matches, you can use the predefined variable #-, which stores the position where your match begins:
perl -lwe '$str = "foobar"; if ($str =~ /bar/) { print $-[0] }'
However, I suggest you tell us what it is you are trying to do. Using string offsets is not the best perl tool in the box, and I suspect there are much better ways of solving your problem.
Perhaps look at pos?
use strict;
use warnings;
my $str = 'foobarbaz';
$str =~ /bar/g;
print pos($str), "\n";
print substr( $str, pos($str) ), "\n";
pos($str) = pos($str) + 2;
print substr( $str, pos($str) ), "\n";