在Perl中以不同的格式获取日期?

时间:2013-03-05 07:31:14

标签: regex perl date

我需要获得可能采用3种格式的日期。

  1. 2012年11月20日
  2. 2012年11月20日
  3. 2012年11月20日
  4. 我怎样才能在Perl中实现这一点。我正在尝试RegEx来获得我想要的东西。这是我的代码。

    my @dates = ("Mon 11/20/2012","2012.11.20","20-11-2012"); #array values may vary in every run 
    foreach my $date (@dates){
        $date =~ /[-.\/\d+]/g;
        print "Date: $date \n";
    }
    

    我想要输出。 (上面的代码不会打印任何内容)

    Date: 11/20/2012
    Date: 2012.11.20
    Date: 20-11-2012
    

    我哪里错了?请帮忙。感谢

    注意:我想在不使用任何CPAN模块的情况下实现此目的。我知道有很多CPAN模块可以提供我想要的东西。

4 个答案:

答案 0 :(得分:3)

您的代码几乎可以生成您想要的内容。我假设您的输入有点复杂,或者您发布了实际上没有运行的代码。

无论哪种方式,问题都是这个

$date =~ /[-.\/\d+]/g;

首先,你的加倍数在字符类中:它应该在它之后。其次,它只是一个模式匹配,你需要在列表上下文中使用它,并存储它的返回值:

my ($match) = $date =~ /[-.\/\d]+/g;
print "Date: $match\n";

然后它将返回找到的包含破折号,句号,斜线或数字中的一个或多个的第一个字符串。请注意,它也会匹配其他内容,因为它是一个相当不严格的正则表达式。

为什么会这样?因为在使用全局/g修饰符时,列表上下文中的模式匹配会返回匹配列表。

答案 1 :(得分:2)

我强烈建议使用DateTime::Format::Strptime模块,它具有丰富的功能。不仅要考虑解析字符串,还要检查日期是否有效。

答案 2 :(得分:2)

为什么不一次搜索一种格式?

=~ m!(\d{2}/\d{2}/\d{2}|\d{4}\.\d{2}\.\d{2}|\d{2}-\d{2}-\d{4})!

应该做的伎俩。除此之外,还有一个处理日期的模块DateTime

答案 3 :(得分:1)

尝试依次匹配格式。下面的正则表达式与您允许的任何分隔符(/.-)匹配,然后通过反向引用(\2\3)需要相同的分隔符。否则,您有三个可能的分隔符乘以一年中两个可能的位置,以便在您的模式中创建六个替代方案。

#! /usr/bin/env perl

use strict;
use warnings;

#array values may vary in every run
my @dates = ("Mon 11/20/2012","2012.11.20","20-11-2012");

my $date_pattern = qr<
  \b  # begin on word boundary
  (
    (?:           [0-9][0-9] ([-/.]) [0-9][0-9] \2 [0-9][0-9][0-9][0-9])
  | (?: [0-9][0-9][0-9][0-9] ([-/.]) [0-9][0-9] \3 [0-9][0-9])
  )
  \b  # end on word boundary
>x;

foreach my $date (@dates) {
  if (my($match) = $date =~ /$date_pattern/) {
    print "Date: $match\n";
  }
}

输出:

Date: 11/20/2012
Date: 2012.11.20
Date: 20-11-2012

在我第一次尝试上面的代码时,我在YYYY-MM-DD替代方案中有\2我应该有\3,但未能匹配。为了让我们计算括号,version 5.10.0 added named capture buffers

  
      
  • 命名捕获缓冲区
  •   
     

现在可以在模式中命名捕获括号,并按名称引用捕获的内容。命名语法为(?<NAME>....)。可以使用\k<NAME>语法反向引用命名缓冲区。在代码中,新的魔法哈希%+%-可用于访问捕获缓冲区的内容。

使用这个方便的功能,上面的代码变为

#! /usr/bin/env perl

use 5.10.0;  # named capture buffers

use strict;
use warnings;

#array values may vary in every run
my @dates = ("Mon 11/20/2012","2012.11.20","20-11-2012");

my $date_pattern = qr!
  \b  # begin on word boundary
  (?<date>
    (?:           [0-9][0-9] (?<sep>[-/.]) [0-9][0-9] \k{sep} [0-9][0-9][0-9][0-9])
  | (?: [0-9][0-9][0-9][0-9] (?<sep>[-/.]) [0-9][0-9] \k{sep} [0-9][0-9])
  )
  \b  # end on word boundary
!x;

foreach my $date (@dates) {
  if ($date =~ /$date_pattern/) {
    print "Date: $+{date}\n";
  }
}

并产生相同的输出。

上面的代码仍然包含很多重复。使用(DEFINE)特殊情况与命名捕获相结合,我们可以使模式更好。

#! /usr/bin/env perl

use 5.10.0;

use strict;
use warnings;

#array values may vary in every run
my @dates = ("Mon 11/20/2012","2012.11.20","20-11-2012");

my $date_pattern = qr!
  \b (?<date> (?&YMD) | (?&DMY)) \b

  (?(DEFINE)
    (?<SEP>  [-/.])
    (?<YYYY> [0-9][0-9][0-9][0-9])
    (?<MM>   [0-9][0-9])
    (?<DD>   [0-9][0-9])
    (?<YMD>  (?&YYYY) (?<sep>(?&SEP)) (?&MM) \k<sep> (?&DD))
    (?<DMY>  (?&DD)   (?<sep>(?&SEP)) (?&MM) \k<sep> (?&YYYY))
  )
!x;

foreach my $date (@dates) {
  if ($date =~ /$date_pattern/) {
    print "Date: $+{date}\n";
  }
}

是的,名为DMY的子模式也匹配MDY表单中的日期。现在它就足够了,you ain’t gonna need it