用于打印的 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

这个问题的棘手部分是,在收到下一个值之前,您不知道如何打印以前的值。

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

它不是最紧凑的代码,但我相信清晰度比压缩更重要,尤其是在正确的情况下(如有必要,稍后会进行优化或压缩)。

BEGIN { FS = "," }
function print_range()
{
if (lo == hi)
printf "%s%d", pad, lo
else
printf "%s%d-%d", pad, lo, hi
pad = ","
}
{
lo = ""
hi = ""
pad = ""
for (i = 1; i <= NF; i++)
{
if ($i == "")   # Ignore empty fields - could report them
continue
else if (lo == "")
hi = lo = $i
else if ($i == hi + 1)
hi = $i
else
{
# Previous range complete - print it
print_range()
lo = hi = $i 
}
}
print_range()
print ""
}

我使用了一个包含上述代码的文件script.awk,并调用awk -f script.awk data*来生成如下所示的输出。

以下是一些示例数据(紧密基于问题中的数据):

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

第一行有一个尾随逗号;其他行没有。 第三行和后续行缺少条目 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 -e 'BEGIN { ORS = ","; RS = ","; O = -1 } { if(N + 1 == $1) { N += 1;} else { if (O != -1 && O != N) { print O,"-",N } else { print N; } O = $1; N = $1; } }'

试试这个:

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,

下面是一个非常短的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

采取以下步骤:

  1. 清理:删除空字段

    {gsub(/,+/,","); gsub(/^,|,$/,"")}
    
  2. 检查:检查上一个字段是否比当前字段
  3. 少一个,下一个字段是否比当前字段多一个。将此信息存储在新阵列中。

    {delete a; for(i=2;i<NF;++i) a[i]=($i-$(i-1) == 1 && $(i+1)-$i == 1)}
    
  4. 删除
  5. 如果上一个条件为真,则删除该值

    {for(i=1;i<=NF;++i) if (a[i]) $i=""}
    
  6. 字符:多个逗号表示一个序列,请用连字符替换它们。

    {gsub(/,,+/,"-"); print}
    

步骤 2 和 3 实际上可以通过使用两个临时变量来组合(a跟踪前一个字段的原始值,b真正的临时变量)

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

最新更新