使用 Regex / Perl 解析日期列

Posted

技术标签:

【中文标题】使用 Regex / Perl 解析日期列【英文标题】:Parse Date Column using Regex / Perl 【发布时间】:2012-08-07 15:26:11 【问题描述】:

我正在编写一个 perl 脚本来解析来自标准输入的制表符分隔数据。

该脚本会删除前导和尾随空格,将包含字符串“NULL”的所有字段清空,并将日期列从“MMM DD YYYY HH:MM:SS:SSSAM”格式重新格式化为“YYYYMMDD”格式。

示例输入:

93092   Apr  1 2010 12:00:00:000AM      59668370.60702875
22341   Apr  1 2010 12:00:00:000AM      51309196.84639429
27844   Apr  1 2010 12:00:00:000AM      NULL
150465  Apr 22 2010 12:00:00:000AM      19706190.97586569
119364  Jul  20 2010 12:00:00:000AM      16335977.41009162

目标输出:

93092|20100401|59668370.60702875
22341|20100401|51309196.84639429
27844|20100401|
150465|20100422|19706190.97586569
119364|20100720|16335977.41009162

该脚本接受一个参数,该参数表示具有需要转换的日期的列。在上面的示例中,我会使用“1”作为参数调用,因为第二列是需要转换的日期。多个列将由逗号分隔的列表表示。

这是我迄今为止能够做到的。

#!/usr/bin/perl
my @date_cols = split(/,/, $ARGV[0]);

while (<STDIN>) 
   my @fields = split(/\t/, $_, -1);
   for (@fields) 
      s/^\s+//;
      s/\s+\z//;
      s/^NULL\z//;
   
   for (@fields[@date_cols]) 
##NEED HELP WITH DATE FORMATTING
   

   print(join('|', @fields), "\n");

【问题讨论】:

您的文件是制表符分隔的吗?否则,您的第二列(按空格)只是月份,而不是整个日期。 我建议使用像DateTime::Format::Strptime 这样的模块来处理这个细节。从 DateTime 对象格式化日期变得微不足道。 或者 Time::Piece - 在 Perl 核心发行版中。 我在这台机器上没有管理员,所以我需要使用标准发行版中的东西。我从未使用过模块(例如 Time::Piece),在此之前我希望使用正则表达式。 【参考方案1】:

使用Time::Piece 很简单,很容易为您提供日期格式。 strptime 函数可让您定义要使用的模式; strftime 函数可让您生成所需的输出格式。考虑:

use Time::Piece;
my $date = "Apr  1 2012 12:00:00AM";
my $t = Time::Piece->strptime($date,"%b %d %Y %H:%M:%S%p");
print $t->strftime("%Y%m%d\n");

这种方法的一个很好的特点是,一个或两个空格分隔月份和日期字段都没有关系。结果是一样的。

【讨论】:

日期格式在秒数中包含额外的 :000。我能够将您的示例更改为在 strptime 函数中包含“:000”。 my $t = Time::Piece-&gt;strptime($_,"%b %d %Y %H:%M:%S:000%p"); 这部分现在可以工作了。我尝试将其插入原始脚本,但出现错误“在 /usr/lib/perl5/5.10.0/x86_64-linux-thread-multi/Time/Piece.pm 第 470 行解析时间时出错, 第 1 行。” for (@fields[@date_cols]) my $t = Time::Piece-&gt;strptime($_,"%b %d %Y %H:%M:%S:000%p"); my $s = $t-&gt;strftime("%Y%m%d\n"); splice(@fields,@date_cols,1,$s); 其实...这很好用。我找到了导致我的问题的类型-o。感谢您的帮助!【参考方案2】:

根据上面 Dave Cross 的建议使用Time::Piece:

use Time::Piece;

while (<STDIN>) 
    # Split each row into columns by white space
    my @fields = split /\s+/;

    # Rebuild the date ("Apr 1 2010") from columns 2 through 4
    my $time_field = join ' ', @fields[1..3];

    # Parse the date - see man strptime
    my $date = Time::Piece->strptime($time_field, '%B %d %Y');

    # Format the output - see man strftime
    print join '|', $fields[0], $date->strftime('%Y%m%d'), $fields[5];

Regex 是一个非常出色的工具,但日期很难看(甚至令人恐惧)。只要有可能,我更喜欢使用已经存在的库来解析它们。

【讨论】:

我不确定在空白处解析是否是最佳选择,因为输入文件中可能有多个日期字段,并且计算出哪些列代表日期的数学运算会很快变得复杂。

以上是关于使用 Regex / Perl 解析日期列的主要内容,如果未能解决你的问题,请参考以下文章

将列字符串转换/解析为日期时间值 - 熊猫

从 SQL 数据库导入表并按日期过滤行时,将 Pandas 列解析为 Datetime

从 SQL 数据库导入表并按日期过滤行时,将 Pandas 列解析为 Datetime

对从 Quickbase 解析的列应用日期时间操作,以在 pyspark 数据块中获取正确的日期时间格式

将 txt 文件中的日期解析为整数

提取以“st”、“nd”、“rd”、“th”结尾的日期,同时使用 RegEx 将日期与月份交换