2009-07-09 24 views
5

我必须将一个非常大的数据集从一个系统迁移到另一个系统。其中一个“源”列包含日期,但实际上是一个没有约束的字符串,而目标系统要求格式为yyyy-mm-dd的日期。“智能”(原谅)日期解析器?

许多(但不是全部)源日期格式为yyyymmdd。所以,强迫他们到期望的格式,我这样做(在Perl):

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

当源日期移动从“通用” YYYYMMDD远就会出现问题。目标是在放弃之前挽回尽可能多的日期。示例源字符串包括:

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

我可以尝试尽可能多的实例匹配我可以经常连续发现如上面的表达式。

但是有什么更聪明的做法吗?我是不是重新发明轮子?有没有一个图书馆可以做类似的事情?我找不到任何相关的谷歌搜索“原谅日期解析器”。 (任何语言都可以)。

+0

3/4/97 - 是3月4日还是4月3日? – 2009-07-09 10:50:16

+0

取决于您所在的地区。在美国,那是3月4日。在美国之外,可能在4月3日。 – 2009-07-09 16:42:35

+0

我会想象大多数日期工具将有一种方法来设置如何处理像3/4/97的情况下的默认选项。匆匆一瞥,下面列出的至少两个Perl模块都有这样的选项。 – Telemachus 2009-07-09 17:24:51

回答

2

我终于提取了超过200个实际发生在数据集中的日期示例的测试集。有些人很轻微,有些人完全不舒服(例如“01010”)。

我尝试了所有现有的Perl模块,但是成功率太低。我最终潜入我的车轮,成功率超过98%。

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

  • 解析字符串中任意位置的数字组。法语和英语的“月份名称”也被认可。

  • 对于他们每个人,我把它们放在三个桶里:候选人一年,候选人一个月,候选人一天。例如,“13”将位于“可能的年份”存储桶中,并位于“可能的日期”存储桶中。当然,“二月”只会在“月”桶中出现。在每个存储桶中,该值都标有“可信度级别”,这是一个取决于许多事物的任意数字。例如,2010年比10年更合理。

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

  • 按顺序(年,月,日)查找其各自桶中剩余的缺失值,并以最可信度为准。如果是平局,则取最后一个出现在该字符串中的那个(实际上,这些合理性略高)。这个规则在7月3日作为3月7日打破,因为我在法国需要这里。如果情况适用,请从其他存储桶中删除该值。

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

整件事非常启发式,但符合我的要求,最好是有垃圾而不是丢失信息。

4

您是否在寻找Date::Parse模块?

+0

我不知道perl,但至少在C#中,bogstandard DateTime.TryParse()将接受相当多样化的不同日期格式。你应该注意那些不接受的东西,并将它们专门化。在这种情况下,整行可能需要手动处理。 – 2009-07-09 10:58:27

4

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

如果你有不同的格式(例如,前一天和一个月反之亦然),你必须以不同的方式解析它们,一次使用美国日期格式,另一次使用非美国日期格式。这在模糊的时候尤其重要,比如你的3/4/97例子,因为如果它是21/3,它就会失败,你可以告诉格式错误。

[email protected]:~$ 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"; 
}; 
[email protected]:~$ perl date.pl 
March 2004 
2004030100:00:00 
2001 
2001010100:00:00 
3/4/97 
1997040300:00:00 
21/3/1998 
0:00:00 
1

您还可以看看DateTime::Format::Flexible

基于其描述,这是对你的胡同:

如果你曾经不得不使用程序 使你键入在某日某种方式和想法“为什么不能 计算机只是找出我 想要什么日期?”,这个模块是给你的。

DateTime :: Format ::弹性的尝试到 将您给它的任何字符串和解析 它到一个DateTime对象。

我刚刚使用此模块运行了Vinko脚本的一个版本,并得到了类似的结果。除了最后一种情况(1998年3月21日),一切都很好。与Date::Manip一样,您可以通过明确设置参数(european => 1)来相对容易地处理此问题。丹比斯特罗姆的评论显示了这种情况为什么需要人为疏忽。