newbie Perl guy here. I have a Perl script (v5.26.1) that parses a logfile. For reasons beyond my control, the format of each date in the log is this:
"yyyy.mm.dd.hh.mm.ss"
e.g.:
"2022.09.19.16.35.00"
Note that year is always four digits, while month, day, hour, minute, and second are always two digits. (e.g. Sept is 09
)
I need to convert this string into a DateTime object for comparison purposes. I thought this exercise would be a breeze, but five hours and a lot of fruitless Google searches later, and I'm no-where close. Here's my first attempt:
#!/usr/bin/perl
use warnings;
use strict;
sub transStrToDTime
{
# Example format of a date: "2022.09.19.16.35.00"
my $str = @_; # Only one input argument
use DateTime qw( );
my ($y,$m,$d,$h,$m,$s) = $str =~ /^([0-9]{4}).([0-9]{2}).([0-9]{2}).([0-9]{2}).([0-9]{2}).([0-9]{2})\z/
or die;
my $dt = DateTime->new(
year => $y,
month => $m,
day => $d,
hour => $h,
minute => $m,
second => $s,
time_zone => 'local'
);
printf("=-=-=-=-=-=-=->>> \"$dt\"\n");
}
my $str="2022.09.19.16.35.00";
transStrToDTime($str);
Code has a syntax error:
me@ubuntu:/home/me# ./toyPerl.pl
Died at ./toyPerl.pl line 16.
me@ubuntu:/home/me#
Where line 16 is:
my ($y,$m,$d,$h,$m,$s) = $str =~ /^([0-9]{4}).([0-9]{2}).([0-9]{2}).([0-9]{2}).([0-9]{2}).([0-9]{2})\z/
Ugggggggghhhhhhhhh... After reading up on this topic and becoming more confused, I decided to do the conversion manually. I figured that all I had to do is:
split()
the string on.
- Build a new DateTime object with the string tokens as input:
Here's that attempt:
#!/usr/bin/perl
use warnings;
use strict;
my $str="2022.09.19.16.35.00";
my @spl = split('.', $line);
#Lets look at the tokens before we build the DateTime object:
for(my $i = 0; $i <= $#spl; $i ){
print("$i) $spl[$i] \n");
}
Output:
me@ubuntu:/home/me# ./toyPerl.pl
me@ubuntu:/home/me#
No output... meaning that split()
split "2022.09.19.16.35.00"
into zero tokens? Is $str
not a string, then? So what data type could it be?
#!/usr/bin/perl
use warnings;
use strict;
my $str="2022.09.19.16.35.00";
printf("Verifying that \$str is a string:\n");
printf("---> ${ref($str)}\n");
Output:
me@ubuntu:/home/me# ./toyPerl.pl
Verifying that "2022.09.19.16.35.00" is a string:
Can't use string ("") as a SCALAR ref while "strict refs" in use at ./2222toyPerl.pl line 7.
me@ubuntu:/home/me#
Where line 7 is this line:
printf("---> ${ref($str)}\n");
I'm so confused. The error message seems to say that my string isn't a scalar. But I thought strings were scalar in Perl? ("A scalar is most often either a number or a string.") And why is the string reduced to an empty string (""
) in line 7?
Oh man. This post represents half a day's work. Can anyone spot my syntax error in the first method? And why can't I split()
string "2022.09.19.16.35.00"
? Is it not a string or something? Thank you.
CodePudding user response:
When you put
my $str = @_;
It means you put the array in scalar context, and in scalar context, arrays return their size. What you want is to use list context:
my ($str) = @_;
Or better yet, use the idiomatic shift
:
my $str = shift; # automatically uses @_ inside a subroutine
You are also using two $m
variables. Change one of them to something else. Revised code works as expected:
use strict;
use warnings;
sub transStrToDTime {
my ($str) = @_; # Only one input argument
use DateTime qw( );
my ($y,$M,$d,$h,$m,$s) = $str =~ /^([0-9]{4}).([0-9]{2}).([0-9]{2}).([0-9]{2}).([0-9]{2}).([0-9]{2})\z/
or die;
my $dt = DateTime->new(
year => $y,
month => $M,
day => $d,
hour => $h,
minute => $m,
second => $s,
time_zone => 'local'
);
printf("=-=-=-=-=-=-=->>> \"$dt\"\n");
}
my $str="2022.09.19.16.35.00";
transStrToDTime($str);
Output:
=-=-=-=-=-=-=->>> "2022-09-19T16:35:00"
The thing you call a syntax error in the first method is actually not a syntax error. Just your code that says die
if the regex match fails. Note that die
without a message is not terribly informative. You might want to put something more useful there.
In your second case, when you split on '.'
, you are using a wildcard character .
, not a literal period. Hence the entire string is consumed and nothing is left. You might try split /\./
instead.
In the third case, I don't know what you are doing here: ${ref($str)}
You are trying to dereference the return value from ref
? The return value from ref
is just a string, e.g. ARRAY
or SCALAR
. That's not how you use ref
. If you want to know what a variable contains, instead use Data::Dumper
:
use Data::Dumper;
print Dumper $str;
# will print $VAR1 = 1; in your first program ($str is the size of the array @_)
Also, this line:
printf("=-=-=-=-=-=-=->>> \"$dt\"\n");
- Don't use
printf
when you can useprint
.printf
has a special usage. - Instead of escaping quotes inside the string, consider alternatives such as:
print " '$dt' \n";
print qq( "$dt" \n);
use feature 'say';
say " '$dt' ";
CodePudding user response:
In addition to everything TLP said, you can use
use DateTime::Format::Strptime qw( );
my $format = DateTime::Format::Strptime->new(
pattern => '%Y.%m.%d.%H.%M.%S',
strict => 1,
time_zone => 'local',
on_error => 'croak',
));
my $dt = $format->parse_datetime( $str );