-1

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

  1. 2012 年 11 月 20 日
  2. 11.20.2012
  3. 2012 年 11 月 20 日

我怎么能在 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

4 回答 4

3

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

无论哪种方式,问题是这样的

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

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

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

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

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

于 2013-03-05T08:07:55.490 回答
2

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

于 2013-03-05T07:35:38.420 回答
2

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

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

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

于 2013-03-05T07:36:45.307 回答
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";
  }
}

输出:

日期:2012 年 11 月 20 日
日期:2012.11.20
日期:20-11-2012

在我第一次尝试上面的代码时,我\2在 YYYY-MM-DD 替代方案中应该有\3,但无法匹配。为了避免我们计算括号,版本 5.10.0 添加了命名的捕获缓冲区

  • 命名捕获缓冲区

现在可以在模式中命名捕获括号并按名称引用捕获的内容。命名语法是(?<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 的子模式也匹配日期 int MDY 形式。现在它就足够了,你不需要它

于 2013-03-05T12:14:17.117 回答