Perl :: 将字符串“yyyy.mm.dd.hh.mm.ss”转换为 DateTime 对象?

Pet*_*ete 3 string perl datetime type-conversion

这里是 Perl 新手。我有一个解析日志文件的 Perl 脚本 (v5.26.1)。由于我无法控制的原因,日志中每个日期的格式如下:

"yyyy.mm.dd.hh.mm.ss"
Run Code Online (Sandbox Code Playgroud)

例如:

"2022.09.19.16.35.00"
Run Code Online (Sandbox Code Playgroud)

请注意,年始终为四位数字,而月、日、小时、分钟和秒始终为两位数。(例如九月是09

我需要将此字符串转换为 DateTime 对象以进行比较。我以为这个练习会很容易,但是五个小时和大量谷歌搜索之后毫无结果,我还差得很远。这是我的第一次尝试:

#!/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);
Run Code Online (Sandbox Code Playgroud)

代码有语法错误:

me@ubuntu:/home/me# ./toyPerl.pl
Died at ./toyPerl.pl line 16.
me@ubuntu:/home/me#
Run Code Online (Sandbox Code Playgroud)

其中第 16 行是:

    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/
Run Code Online (Sandbox Code Playgroud)

Uggggggghhhhhhhhh...在阅读了这个主题并变得更加困惑之后,我决定手动进行转换。我想我所要做的就是:

  • split()上的字符串.
  • 使用字符串标记作为输入构建一个新的 DateTime 对象:

这是这次尝试:

#!/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");
}
Run Code Online (Sandbox Code Playgroud)

输出:

me@ubuntu:/home/me# ./toyPerl.pl
me@ubuntu:/home/me#
Run Code Online (Sandbox Code Playgroud)

没有输出...意味着split()分裂"2022.09.19.16.35.00" 成零个标记? 那么不是$str字符串吗?那么它可能是什么数据类型呢?

#!/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");
Run Code Online (Sandbox Code Playgroud)

输出:

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#
Run Code Online (Sandbox Code Playgroud)

其中第 7 行是这一行:

printf("---> ${ref($str)}\n");
Run Code Online (Sandbox Code Playgroud)

我很困惑。错误消息似乎表明我的字符串不是标量。但我认为 Perl 中的字符串是标量?(“标量通常是数字或字符串。"" ”)为什么第 7 行字符串被缩减为空字符串 ( )?

天啊。这篇文章代表了半天的工作。有人能发现我在第一种方法中的语法错误吗?为什么我不能split()串起来"2022.09.19.16.35.00"?不是字符串什么的吗?谢谢。

TLP*_*TLP 7

当你把

my $str = @_;
Run Code Online (Sandbox Code Playgroud)

这意味着您将数组放入标量上下文中,并且在标量上下文中,数组返回其大小。您想要的是使用列表上下文:

my ($str) = @_;
Run Code Online (Sandbox Code Playgroud)

或者更好的是,使用惯用语shift

my $str = shift;   # automatically uses @_ inside a subroutine
Run Code Online (Sandbox Code Playgroud)

您还使用两个$m变量。将其中之一更改为其他内容。修改后的代码按预期工作:

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);
Run Code Online (Sandbox Code Playgroud)

输出:

=-=-=-=-=-=-=->>> "2022-09-19T16:35:00"
Run Code Online (Sandbox Code Playgroud)

在第一个方法中您所说的语法错误实际上并不是语法错误。只是您的代码表明die正则表达式匹配是否失败。请注意,die没有消息并不能提供太多信息。您可能想在那里放一些更有用的东西。

在第二种情况下,当您拆分 时'.',您使用的是通配符.,而不是文字句点。因此,整个字符串都被消耗掉了,没有留下任何东西。你可以尝试split /\./一下。

在第三种情况下,我不知道你在这里做什么:${ref($str)}你试图取消引用ref?的返回值。返回值ref只是一个字符串,例如ARRAYor SCALAR。这不是你使用的方式ref。如果您想知道变量包含什么内容,请使用Data::Dumper

use Data::Dumper;
print Dumper $str;
# will print $VAR1 = 1; in your first program ($str is the size of the array @_)
Run Code Online (Sandbox Code Playgroud)

另外,这一行:

    printf("=-=-=-=-=-=-=->>> \"$dt\"\n");
Run Code Online (Sandbox Code Playgroud)
  1. printf能用的时候就不要用printprintf有特殊用途。
  2. 不要在字符串内转义引号,而是考虑替代方案,例如:
my $str = @_;
Run Code Online (Sandbox Code Playgroud)

  • 回复“*当你可以使用`print`时不要使用`printf`。*”,或者更确切地说,如果你要使用`printf`,请正确使用`printf`:`printf("=-=-=- =-=-=-=-&gt;&gt;&gt; \"%s\"\n", $dt);` 但是,是的,这里的 `print` 更容易,而 `say` 也更容易。 (3认同)

ike*_*ami 7

除了 TLP 所说的一切之外,您还可以使用

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 );
Run Code Online (Sandbox Code Playgroud)

  • 这显然是正确的解决方案,我的回答更多的是关于原始代码有什么问题。 (2认同)