用于打印的awk选择性页码

用于打印的awk选择性页码,awk,Awk,我有一个字符串,其中包含要打印的.pdf文件的页码,但我希望最小化字符串的长度,以便用破折号替换所有连续的数字,以便使用awk进行选择性打印。有谁能帮我找到awk的密码吗 从 1,3,4,5,6,7,8,9,11,12,13,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,53 进入 1,3-13,15-51,53 试试这个

我有一个字符串,其中包含要打印的.pdf文件的页码,但我希望最小化字符串的长度,以便用破折号替换所有连续的数字,以便使用awk进行选择性打印。有谁能帮我找到awk的密码吗

1,3,4,5,6,7,8,9,11,12,13,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,53

进入

1,3-13,15-51,53

试试这个:

printf "1,3,4,5,6,7,8,9,11,12,13,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,53," |
  awk '
  function handleRange () {
     if (previous == 0) {
       first=$0
     } else if (previous != ( $0 - 1 )) {
       if ((previous - first) == 0) {
         print previous
       } else if ((previous - first) == 1) {
         print first ORS previous
       } else {
         print first "-" previous
       }
       first=$0
     }
     previous=$0
  }
   /[0-9]/ { handleRange(); }
   END     { handleRange(); }
  ' RS=, ORS=,
输出:

1,3-9,11-13,15-51,53,

这个问题的棘手之处在于,在收到下一个值之前,您不知道如何打印上一个值

这里有一个脚本,当输入多行数据时,它可以正常工作,将每行数据作为一组单独的数字进行处理。它完全忽略空字段(前导、相邻或尾随逗号)。它假定每行上的字段都是数字,并按升序排序。它实际上不适用于数据中的负数(使用破折号分隔范围的格式变得笨拙-但数据是正确的),但它对零很满意

这并不是可能的最紧凑的代码,但我相信清晰比压缩更重要,尤其是在正确使用时(如果有必要,优化或压缩会在以后进行)

第一行后面有一个逗号;其他人没有。第三行和后续行缺少条目29和32,因此数据中存在2个元素范围30-31。最后两行的开头是多元素范围,而不是单个项目;最后一行末尾有一个多元素范围,而不是单个项目

脚本的输出为:

1,3-9,11-13,15-51,53
1,3-9,11-13,15-51,53
1,3-9,11-13,15-28,30-31,33-51,53
3-9,11-13,15-28,30-31,33-51,53
3-9,11-13,15-28,30-31,33-51

很容易调整打印以检测是否
hi==lo+1
,并决定打印逗号分隔的值,而不是破折号分隔的值(如果首选)。

以下是一个非常简短的awk程序,可以实现这一点:

awk 'BEGIN{FS=OFS=","}
     {gsub(/,+/,","); gsub(/^,|,$/,"")}
     {delete a; for(i=2;i<NF;++i) a[i]=($i-$(i-1) == 1 && $(i+1)-$i == 1)} 
     {for(i=1;i<=NF;++i) if (a[i]) $i=""}
     {gsub(/,,+/,"-"); print}' file
  • 检查:检查上一个字段是否比当前字段少一个,下一个字段是否比当前字段多一个。将此信息存储在新数组中

    {delete a; for(i=2;i<NF;++i) a[i]=($i-$(i-1) == 1 && $(i+1)-$i == 1)}
    
  • 通过使用两个临时变量(
    a
    跟踪上一个字段的原始值,以及
    b
    a真正的临时变量),步骤2和步骤3实际上可以组合在一起

    awk'BEGIN{FS=OFS=“,”}
    {gsub(/,+/,“,”);gsub(/^,|,$/,”)}
    
    {a=$1;for(i=2;i)是在一行上显示的数字吗?它们是否用逗号分隔?如果有多行输入,是否应分别处理每行?数字是否按顺序显示(排序)?如果输入是
    93,96,97100
    ,您想要什么作为输出?您可以得到
    93,96-97100
    93,96,97100
    。顺便说一句,
    10
    从数据中丢失;大概预期的输出是
    1,3-9,11-13,15-51,53
    。做得好,除非有2个数字范围(N和N+1)例如在
    93,96,97100
    中。然后您输出输入,而不是
    93,96-97100
    。当然,这是测试数据未涵盖的边缘情况之一,因此不清楚哪种情况是首选。我在回答中明确说明了这一点。由于您检测范围的方式,因此很难使代码适应spot 2-元素范围并将其连字号-但这可能无关紧要。我没想到代码会如此复杂,这一个很好。对于打印来说,数字之间是破折号还是逗号并不重要,只要字符串减少了这么多。@sdf可以随意接受和/或向上投票您最喜欢的任何答案。
    1,3-9,11-13,15-51,53
    1,3-9,11-13,15-51,53
    1,3-9,11-13,15-28,30-31,33-51,53
    3-9,11-13,15-28,30-31,33-51,53
    3-9,11-13,15-28,30-31,33-51
    
    awk 'BEGIN{FS=OFS=","}
         {gsub(/,+/,","); gsub(/^,|,$/,"")}
         {delete a; for(i=2;i<NF;++i) a[i]=($i-$(i-1) == 1 && $(i+1)-$i == 1)} 
         {for(i=1;i<=NF;++i) if (a[i]) $i=""}
         {gsub(/,,+/,"-"); print}' file
    
    {gsub(/,+/,","); gsub(/^,|,$/,"")}
    
    {delete a; for(i=2;i<NF;++i) a[i]=($i-$(i-1) == 1 && $(i+1)-$i == 1)}
    
    {for(i=1;i<=NF;++i) if (a[i]) $i=""}
    
    {gsub(/,,+/,"-"); print}
    
    awk 'BEGIN{FS=OFS=","}
         {gsub(/,+/,","); gsub(/^,|,$/,"")}
         {a=$1; for(i=2;i<NF;++i) {b=$i; $i=($i-a == 1 && $(i+1)-$i == 1) ? "" : b; a=b}}
         {gsub(/,,+/,"-"); print}' file