一个“智能”(宽容)日期解析器?

时间:2009-07-09 10:27:04

标签: regex perl parsing date

我必须将一个非常大的数据集从一个系统迁移到另一个系统。其中一个“source”列包含一个日期,但实际上是一个没有约束的字符串,而目标系统要求使用yyyy-mm-dd格式的日期。

许多(但不是全部)源日期格式为yyyymmdd。所以为了强制它们达到预期的格式,我做(在Perl中):

return "$1-$2-$3" if ($val =~ /(\d{4})[-\/]*(\d{2})[-\/]*(\d{2})/);

当源日期远离“通用”yyyymmdd时出现问题。目标是在放弃之前尽可能多地挽救日期。示例源字符串包括:

21/3/1998, 2004年3月, 2001年, 97年3月4日

我可以尝试通过一系列正则表达式(例如上面的表达式)来匹配尽可能多的示例。

但有更聪明的事情吗?我不是在重新发明轮子吗?在某个地方有类似的东西吗?我找不到任何相关的谷歌搜索“原谅日期解析器”。 (任何语言都可以。)

5 个答案:

答案 0 :(得分:4)

您在寻找Date::Parse模块吗?

答案 1 :(得分:4)

Date::Manip是你的朋友,因为它只有四分之一的失败,因为它采用美国格式,使用Date_Init你可以得到4分中的4分。

如果您有不同的格式(即前一个月,反之亦然),您必须以不同的方式解析它们,一次使用美国日期格式,另一种使用非美国日期格式。这很不明确,就像你的3/4/97例子一样,因为如果它是21/3它就会失败并且你可以告诉格式是错误的。

vinko@mithril:~$ more date.pl
use strict;
use warnings;
use Date::Manip;

my @a;
push @a, "March 2004";
push @a, "2001";
push @a, "3/4/97";
push @a, "21/3/1998";
Date_Init("DateFormat=non-US");
for my $d (@a) {
    print "$d\n";
    print ParseDate($d)."\n";
};
vinko@mithril:~$ perl date.pl
March 2004
2004030100:00:00
2001
2001010100:00:00
3/4/97
1997040300:00:00
21/3/1998
1998032100:00:00

答案 2 :(得分:2)

我最终提取了一组200多个实际出现在数据集中的日期的测试集。有些是轻度行为不端,有些人完全生病(例如“01010”)。

我尝试了所有现有的Perl模块,但成功率太低了。我最终潜入了一个重新发明的轮子,成功率超过了98%。

我的算法是一系列越来越模糊的识别器,从严格有效的日期下降到总猜测区域开始。第一个返回“成功”结果的人获胜。在该堆栈的中间,我有“主”识别器,它做了这样的事情:

  • 在任何地方解析字符串中的数字集。法语和英语的“月份名称”也被认可。

  • 对于他们每个人,我把他们分成三个桶:一年的候选人,一个月的候选人,一天的候选人。例如,“13”将在“可能的年份”桶中,并且在“可能的一天”桶中。 “二月”当然只会进入“月”桶。在每个存储桶中,该值都标有“似是而非等级”,这是一个取决于许多事物的任意数字。例如,2010年比10年更合理。

  • 查看三个桶中的每个桶。如果其中任何一个只有一个项目,则该该桶的值。它也被从其他桶中移除。

  • 按顺序(年,月,日)查找各自存储桶中剩余的缺失值,并采用具有最高合理性的值。如果是平局,请取出弦中最后一个(实际上,那些具有稍高的合理性)。正如我在法国需要的那样,这条规则将于7月3日打破7月3日。如果适用,请从其他存储桶中删除该值。

  • 如果缺少任何值,请使用默认值(例如,我使用8191作为默认年份,是目标系统中允许的最大值)。

整个事情是非常具有启发性的,但符合我的要求,垃圾比丢失信息更好。

答案 3 :(得分:1)

您也可以查看DateTime::Format::Flexible

根据它的描述,它就在你的小巷里:

  

如果您曾经使用过程序   这使你输入日期a   某种方式和想法“为什么不能   电脑只是弄清楚我约会的日期   想要?“,这个模块适合你。

     

DateTime :: Format ::灵活尝试   拿你给它的任何字符串并解析   它成为一个DateTime对象。

我刚刚使用这个模块运行了一个Vinko脚本版本,并得到了类似的结果。一切都很好,除了最后一个案例(21/3/1998)。与Date::Manip一样,您可以通过显式设置参数(european => 1)来相对轻松地处理此问题。 Danbystrom的评论显示了为什么这类案件需要人为监督。

答案 4 :(得分:0)

它不是perl,但this .NET library将解析各种日期/时间字符串。