尝试依次匹配格式。下面的正则表达式匹配您允许的任何分隔符(/
、、.
或-
),然后通过反向引用(\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 形式。现在它就足够了,你不需要它。