我需要编写一个从字符串中提取Date的正则表达式。
这是一个字符串示例:
日期:2014年2月11日星期二11:01:57 +0100(CET)
日期:2014年2月9日星期二11:01:57 +0100(CET)
以下是我的尝试:
$str =~ /Date.+(\d+\s[a-zA-Z]{3}\s\d{4}).+(CET)/;
print $1;
$str =~ /Date.+(\d{1,2}\s[a-zA-Z]{3}\s\d{4}).+(CET)/;
print $1
当我有两位数的日子时,两个都失败了。我尝试了不同的变化,但没有成功。
我能够使用以下内容:
$str =~ /Date.+(\s\d+\s[a-zA-Z]{3}\s\d{4}).+(CET)/;
my $date = $1;
$date =~ s/^\s//;
不幸的是,这不是一个很好的解决方案。我确信有一种方法可以在正则表达式中实现它我找不到它。
请告知。
谢谢,
-Andrey
答案 0 :(得分:12)
我认为你应该使用Date::Parse。
#!/usr/bin/perl
use strict;
use warnings;
use Date::Parse;
my $date = "Tue, 11 Feb 2014 11:01:57 +0100 (CET)";
my $time = str2time($date);
# or like this
my @arr = strptime($date);
如果你想坚持使用正则表达式,就像Sean所指出的那样,你必须要知道第一个.+
匹配CET
以外的所有内容,不会留下捕捉括号的任何内容。代码的替代方法可以简单:
if ($str =~ /Date: (.*) \(CET\)/) {
print "$1\n";
}
编辑:
正如@Dave在评论中提到的那样,使用Time::Piece解析字符串(包含在标准Perl模块中),如果您想要具体说明字符串的格式,可能会更好。在那种情况下,
my $t = Time::Piece->strptime($date, "Date: %a, %d %b %Y %T %z (%Z)");
会给你Time :: Piece对象。
答案 1 :(得分:3)
正则表达式中的第一个.+
贪婪地匹配日期中两个数字中的第一个,只留下一个用于捕获组。只需将其更改为.+?
即可匹配尽可能少的字符。
顺便提一下,正则表达式末尾的(CET)
与字符串"(CET)"
不匹配,只是"CET"
,因为未转义的括号形成第二个捕获组。您可能想说\(CET\)
。
答案 2 :(得分:3)
对于简单匹配(即如果你在捕获它之后不需要操纵日期),我喜欢Regexp::Common::time:
use Regexp::Common qw(time);
my $format = '%a, %_d %b %Y %T %Z';
while (<DATA>) {
say $1 if /($RE{time}{strftime}{-pat => $format})/;
}
__DATA__
Date: Tue, 11 Feb 2014 11:01:57 +0100 (CET)
Date: Tue, 9 Feb 2014 11:01:57 +0100 (CET)
Tue, 11 Feb 2014 11:01:57 +0100
Tue, 9 Feb 2014 11:01:57 +0100
请注意,Regexp :: Common :: time支持来自POSIX strftime
的大多数,但不是全部格式代码。在上面的示例中,我必须使用%Z
代替%z
以及非标准%_d
。
答案 3 :(得分:2)
如果输入始终采用您提供的格式,则应该有效:
my $string = 'Date: Tue, 11 Feb 2014 11:01:57 +0100 (CET)';
my ($day, $month, $year) = $string =~ /Date:.+?(\d+)\s(.+?)\s(\d+)/;
print "$day\t$month\t$year\n";
打印:
11 Feb 2014
答案 4 :(得分:2)
Time::Piece已包含在标准Perl发行版中。它有一个strptime
方法,可以简化这一过程。
#!/usr/bin/perl
use strict;
use warnings;
use 5.010;
use Time::Piece;
my $fmt = 'Date: %a, %d %b %Y %H:%M:%S %z (%Z)';
while (<DATA>) {
chomp;
my $dt = Time::Piece->strptime($_, $fmt);
# use $dt->strftime(...) for more control of output
say $dt->datetime;
}
__DATA__
Date: Tue, 11 Feb 2014 11:01:57 +0100 (CET)
Date: Tue, 9 Feb 2014 11:01:57 +0100 (CET)
答案 5 :(得分:1)
你可能真的具体:
\s((?:Mon|Tue|Wed|Thu|Fri|Sat|Sun),\s\d+\s(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s\d+\s\d+:\d+:\d+\s\+\d+\s\(\w+\))
答案 6 :(得分:1)
我在小型日志解析器中使用了很多...
在脚本开始时,创建两个变量:%month
和$mntregex
:
use POSIX qw|strftime setlocale LC_TIME|;
setlocale( LC_TIME, "C" );
my %month;
map {$month{strftime("%b",1,1,1,1,$_,1,-1,-1,-1)}=$_;} (0..13);
my $mntregex = join( "|", keys %month );
那么现在你可以简单地grep和解析日期:
# Format ref: Date: Tue, 11 Feb 2014 11:01:57 +0100 (CET)
/^Date:\s+\S+,\s+(\d+)\s+($mntregex)\s+(\d+)\s+(\d+):(\d+):(\d+)\s/ && do {
$tstamp=strftime("%s",$6,$5,$4,$3,$month{$2},$1-1900,-1,-1,-1);
...
这不依赖于库,而且比
更快但没有经过适当的检查:
use Date::Parse;
$tstamp=str2time($1) if /^Date: (.*)$/;