我需要获得可能采用3种格式的日期。
我怎样才能在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模块可以提供我想要的东西。
答案 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。