【问题标题】:Finding Contiguous Ranges寻找连续范围
【发布时间】:2016-04-15 23:58:41
【问题描述】:

我想查找给定一组日期的连续范围

给出以下示例

2016-01-01
2016-01-02
2016-01-03
2016-01-04
2016-01-05
2016-01-06
2016-01-08
2016-01-09
2016-01-10
2016-01-11
2016-01-12
2016-01-15
2016-01-16
2016-01-17
2016-01-20
2016-01-21
2016-01-30
2016-01-31
2016-02-01

我希望得到以下结果

2016-01-01-2016-01-06
2016-01-08-2016-01-12
2016-01-15-2016-01-17
2016-01-20-2016-01-21
2016-01-30-2016-01-31
2016-02-01-2016-02-01

我已经遇到过this 问题,这几乎与我想要的相反,但使用的是整数。 我制定了以下适用于整数的内容。

awk 'NR==1 {l=$1; n=$1} {if ($1==n){n=$1+1} else{print l"-"n-1; l=$1 ;n=$1+1} } END {print l"-"$1}' file.txt

【问题讨论】:

  • hmmm 即使你做到了,你还有一个问题要解决,如果这个月有 30 天呢?最后一天(所以是第 30 天)也会被打印出来,因为人们认为下一天在文本中不存在。另外,如果一个月有 29 或 31 天怎么办,为了完成这项工作,您必须“告诉”脚本哪个月有多少天,以便它知道什么时候不打印该月的最后一天。
  • 如果我们越过了一个月的界限,就可以开始一个新的范围。我已经更新了示例和结果以反映这一点。此外,如果日期可以转换为序数值,这将不是问题

标签: bash awk


【解决方案1】:

使用 GNU awk 获取 mktime():

$ cat tst.awk
BEGIN { FS=OFS="-" }
{ currSecs = mktime( $1" "$2" "$3" 0 0 0" ) }
(currSecs - prevSecs) > (24*60*60) {
    if (NR>1) {
        print startDate, prevDate
    }
    startDate = $0
}
{ prevSecs = currSecs; prevDate = $0 }
END { print startDate, prevDate }

$ awk -f tst.awk file
2016-01-01-2016-01-06
2016-01-08-2016-01-12
2016-01-15-2016-01-17
2016-01-20-2016-01-21
2016-01-30-2016-02-01

如果您不关心在月份变化时重新启动范围(如您的预期输出和问题下的评论所示),则使用任何 awk:

$ cat tst.awk
BEGIN { FS=OFS="-" }
{ currYrMth = $1 FS $2; currDay = $3 }
(currYrMth != prevYrMth) || ((currDay - prevDay) > 1) {
    if (NR>1) {
        print startDate, prevDate
    }
    startDate = $0
}
{ prevYrMth = currYrMth; prevDay = currDay; prevDate = $0 }
END { print startDate, prevDate }

$ awk -f tst.awk file
2016-01-01-2016-01-06
2016-01-08-2016-01-12
2016-01-15-2016-01-17
2016-01-20-2016-01-21
2016-01-30-2016-01-31
2016-02-01-2016-02-01

【讨论】:

    【解决方案2】:

    如果你有 GNU Awk,你可以使用它的time functions

    gawk -F - 'NR==1 || $1 "-" $2 "-" $3 != following {
        if (following != "") print start "-" latest;
        start = $1 "-" $2 "-" $3
        this = mktime($1 " " $2 " " $3 " 0 0 0")
      }
      {
        this += 24*60*60
        following = strftime("%F", this)
        latest = $1 "-" $2 "-" $3 }
      END { if (start != latest) print start "-" latest }' filename
    

    单位范围将打印为“2016-04-15-2016-04-15”,这有点麻烦,但如果需要,可以轻松修复。在这种情况下,END 块也有一个错误,但同样,这至少应该让你开始。

    【讨论】:

      【解决方案3】:

      傻瓜:

      #!/bin/awk -f
      BEGIN{
              FS="-"
      }
      {
              a[NR]=mktime($1" "$2" "$3" 0 0 0")
              b[NR]=$2;
              if ( (a[NR-1]+86400) != a[NR] || b[NR-1]!=b[NR] ) {
                      if(NR!=1){
                              print s" - "strftime("%Y-%m-%d",a[NR-1])
                      };
                      s=$0
              }
      }
      END{
              print s" - "$0
      }
      

      使用 awk 时间函数 mktime 创建数组 a,索引为 NR,并将值作为从 $0 派生的纪元时间。

      数组b,索引为NR,值为$2中的月份 如果最后一行的纪元时间 + 86400(+1 天)不等于当前行的纪元时间或前一行的月份,并且当前行不同,除了第一行,打印 s" - "strftime("%Y-%m-%d",a[NR-1] 中的值并重新分配 s 哪个是$0的开始日期

      结束: 打印上次开始时间s和最后一行

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-11-21
        • 2011-07-21
        • 2021-05-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多