Perl:如何从字符串中解析Date

时间:2014-02-11 22:04:29

标签: regex perl

我需要编写一个从字符串中提取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

7 个答案:

答案 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)

自2007年5.10以来,

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+\))

See it work

答案 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: (.*)$/;