【问题标题】:Compress ranges of ranges of numbers in bash压缩bash中数字范围的范围
【发布时间】:2020-05-13 14:28:20
【问题描述】:

我有一个名为“ranges.csv”的 csv 文件,其中包含:

start_range,stop_range  
9702220000,9702220999  
9702222000,9702222999  
9702223000,9702223999  
9750000000,9750000999  
9750001000,9750001999  
9750002000,9750002999  

我正在尝试组合 stop_range=start_range-1 的范围,并将结果输出到另一个名为“ranges2.csv”的 csv 文件中。所以输出将是:

9702220000,9702220999  
9702222000,9702223999  
9750000000,9750002999  

此外,我需要知道有多少范围包含一个压缩范围(例如:对于新范围9750000000,9750002999,我需要知道在压缩之前有 3 个范围)。此信息将帮助我创建一个名为“ranges3.csv”的新 csv 文件,该文件应仅包含其中范围最多的范围(最全面的区域):

    9750000000,9750002999  

我在想这样的事情:

if (stop_range = start_range-1)  
  new_stop_range = start_range-1  

但我不是很聪明,而且我是 bash 脚本的新手。
我知道如何在另一个文件中输出结果,但我需要的功能让我头疼。

【问题讨论】:

  • 欢迎来到 SO,请务必将您的样品包装在代码标签中以明确问题。
  • 不要使用bash 处理数据。至少,使用awk
  • 在这种情况下,我建议你使用awk
  • 首先,您将使用哪种编程语言或外壳工具并不重要,但要了解您的要求。输入 CSV 是否总是一个有序列表,其中一行的停止值总是小于下一行的开始值?此外 - 从来没有一个停止值会溢出起始值吗? (例如 9702220000,9702224999 和下一行 9702222100,9702222500)
  • @TomFreudenberg ,输入 CSV 文件并不总是有序列表。是的,在您的示例中,停止值可能会溢出起始值。

标签: bash csv range


【解决方案1】:

我认为这可以解决问题:

#!/bin/bash

awk '
  BEGIN { FS = OFS = ","}
  NR == 2 {
    start = $1; stop = $2; i = 1
  }
  NR > 2 {
    if ($1 == (stop + 1)) {
      i++; 
      stop = $2
    } else {
      if (++i > max) {
        maxr = start "," stop;
        max = i
      }  
      start = $1
      i = 0
    }
    stop = $2
  }
  END { 
    if (++i > max) {
      maxr =  start "," stop;
    }
    print maxr
  }
' ranges.csv

【讨论】:

  • 如何处理同一个max的多个案例?
  • 我们应该问问 OP。
  • @kvantour ,当碰巧有多个具有相同最大值的案例时,我们应该考虑在这些范围内具有最多数字的最大值(例如:如果新的最大值范围是 9702222000,9702222999另一个新的最大范围是 9702222000,9709999999 我们应该考虑第二个(最大的间隔),因为它有更多的数字)。
  • @PierreFrançois ,我尝试使用您的方法。我使用了一个名为“inputRanges.csv”的未排序输入文件。我用命令 sort -t "," -k 1 -n inputRanges.csv > range.csv 在开头 + 你的 awk 编写了一个脚本。它没有按预期工作。 The input file looks like: start_range,stop_range 9750002000,9750002999 9702220000,9702220999 9750000000,9750000999 9702222000,9702222999 9702223000,9702223999 9750001000,9750001999 9750002000,9750001999 9750002000,9750000999 9750002000,9750009999 9750007000,9750007999 9750008000,9750008999 9750009000,9750009999
  • 我没有预见到未排序列表的情况。
【解决方案2】:

假设您的范围已排序,那么此代码只为您提供合并的范围:

awk 'BEGIN{FS=OFS=","}
     (FNR>1) && ($1!=e+1){print b,e; b=e="" }
     ($1==e+1){ e=$2; next }
     { b=$1; e=$2 }
     END { print b,e }' file    

下面你会得到相同的结果,但范围计数:

awk 'BEGIN{FS=OFS=","}
     (FNR>1) && ($1!=e+1){print b,e,c; b=e=c="" }
     ($1==e+1){ e=$2; c++; next }
     { b=$1; e=$2; c=1 }
     END { print b,e,c }' file

如果你想要最大的,你可以在第三列排序。我不想制定规则来给出计数最多的范围,因为可能有多个。

如果你真的只想要最大合并的所有范围:

awk 'BEGIN{FS=OFS=","}
     (FNR>1) && ($1!=e+1){ 
        a[c] = a[c] (a[c]?ORS:"") b OFS e
        m=(c>m?c:m)
        b=e=c=""
     }
     ($1==e+1){ e=$2; c++; next }
     { b=$1; e=$2; c=1 }
     END { a[c] = a[c] (a[c]?ORS:"") b OFS e
           m=(c>m?c:m)
           print a[m]
     }' file

【讨论】:

  • 您已经完成了一半:您仍然需要计算范围并打印合并范围最多的那个。
  • @PierreFrançois 添加了一个温和的更新。这个问题有点不清楚
  • @kvantour ,是的,你是对的,我不想要只有最大合并的所有范围。抱歉我的解释不好。你的第一种方法是我需要的。 However, the input file can have "bad input ranges" like that : 9750002000,9750002999 9702220000,9702220999 9750000000,9750000999 9702222000,9702222999 9702223000,9702223999 9750001000,9750001999 9750002000,9750001999 9750002000,9750000999 9750002000,9750009999 9750007000,9750007999 9750008000,9750008999 9750009000, 9750009999 ,所以 2 个相等的 start_ranges 和不同的 stop_ranges
  • @kvantour ,或者文件中最后 3 个范围的情况: 9750007000,9750007999 9750008000,9750008999 9750009000,9750009999 已经覆盖在 97500902000,97500099 范围内。另一种情况是 stop_range 大于 start_range(例如:9750002000,9750000999)。
  • 能否请您更新您的问题并明确说明您想要什么?预期输入,预期输出。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多