Regex A「;“聪明的”;(原谅)日期分析器?

Regex A「;“聪明的”;(原谅)日期分析器?,regex,perl,parsing,date,Regex,Perl,Parsing,Date,我必须将一个非常大的数据集从一个系统迁移到另一个系统。其中一个“源”列包含一个日期,但实际上是一个没有约束的字符串,而目标系统要求使用yyyy-mm-dd格式的日期 许多(但不是全部)源日期的格式为yyyymmdd。因此,为了将它们强制转换为预期的格式,我(在Perl中): 当源日期从“通用”yyyymmdd移走时,就会出现问题。我们的目标是在放弃之前尽可能多地保留日期。示例源字符串包括: 1998年3月21日, 2004年3月, 2001, 1997年3月4日 我可以尝试用一系列正则表达式来匹

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

许多(但不是全部)源日期的格式为yyyymmdd。因此,为了将它们强制转换为预期的格式,我(在Perl中):

当源日期从“通用”yyyymmdd移走时,就会出现问题。我们的目标是在放弃之前尽可能多地保留日期。示例源字符串包括:

1998年3月21日, 2004年3月, 2001, 1997年3月4日

我可以尝试用一系列正则表达式来匹配我能找到的尽可能多的例子,比如上面的一个

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

您正在寻找该模块吗?

是您的朋友,因为is四分之一失败,因为它采用US格式,使用Date_Init可以从四分之四中获得四分之四

如果您有不同的格式(例如,前一个月的日期和后一个月的日期),您必须以不同的方式解析它们,一次使用美国日期格式,另一次使用非美国日期格式。这在模棱两可的情况下尤其重要,比如你的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

你也可以看看

根据它的描述,它正是你喜欢的:

如果你曾经使用过一个程序 这让你输入了一个日期 一定的方式和思想“为什么 计算机只是计算出我的日期 “需要吗?”,本模块为您提供

DateTime::Format::灵活地尝试 取你给它的任何字符串并进行解析 将其转换为DateTime对象


我刚才用这个模块运行了一个版本的Vinko脚本,得到了类似的结果。除最后一例(1998年3月21日)外,一切正常。与
Date::Manip
一样,通过显式设置参数(
european=>1
),您可以相对轻松地处理此问题。Danbystrom的评论说明了为什么这样的情况需要人为的监督。

它不是perl,但会解析大量的日期/时间字符串。

我最终提取了一个测试集,其中包含200多个实际发生在数据集中的日期示例。有些人行为稍有不端,少数人完全生病(例如“01010”)

我尝试了所有现有的Perl模块,但成功率太低。我最终投身于一项对我的方向盘进行重新改造的工作,取得了98%以上的成功率

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

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

  • 对于每一个,我都把它们放在三个桶里:一年的候选人,一个月的候选人,一天的候选人。例如,“13”将在“可能年”桶中,在“可能日”桶中。当然,“二月”只会在“月份”的桶里。在每个bucket中,值都被标记为“合理性级别”,这是一个取决于许多事情的任意数字。例如,2010年比10年更合理

  • 看看这三个桶中的每一个。如果其中任何一个只包含一项,则为该存储桶的值。它也从其他桶中移除

  • 按顺序(年、月、日)在各自的存储桶中查找剩余的缺失值,取一个具有最高合理性的值。在平局的情况下,以字符串中最后出现的一个为例(实际上,那些具有稍高的合理性)。这条规则在2010年3月7日被打破,因为我需要在法国。如果情况适用,则从其他存储桶中删除该值

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


整个过程都是非常好的启发,但符合我的要求,即垃圾比信息丢失要好

1997年3月4日-是3月4日还是4月3日?取决于您所在的地区。在美国,那是三月四日。在美国以外的地方,可能是4月3日。我想大多数日期工具都会有一种方式来设置默认选项,以处理类似3/4/97这样的情况。快速浏览一下,下面列出的Perl模块中至少有两个有这样的选项。我不知道Perl,但至少在C#中,标准DateTime.TryParse()可以接受相当不同的日期格式。你应该注意那些它不接受的,并对它们进行特殊处理。在这种情况下,可能整行需要手动处理。说明:“DateTime::Format::Flexible-主要是DateTime::Format::Natural的一个子集,不推荐使用。请改用DateTime::Format::Natural(并在需要时提交补丁以改进其解析;)”我看到了这一点,但我也在模块自己的页面上看到了这一点:“截至2008年3月的DateTime网站将此模块列在“混淆”下,并建议使用DateTime::Format::Natural。不幸的是,我不同意。DateTime::Format::Natural目前在2000多次解析测试中失败。DateTime::Format::Flexible支持与DateTime::Format::Natural不同类型的日期/时间字符串。我认为两者都有实用性。”既然OP要求“宽恕”,我认为这值得一看。
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