如何将正则表达式匹配的数字减1?

如何将正则表达式匹配的数字减1?

我正在尝试编写一个脚本,将行中的每个数字减少“1”,但我得到的都是“0”:

awk '{a=gensub(/([0-9]+)/,"\\1","g",$0);
     if(a~/[0-9]+/) {gsub(/[0-9]+/,a-1,$0);}
     print $0}'

例如字符串:

1,2,3,4-7

应该导致:

0,1,2,3-6

相反我得到:

0,0,0,0-0

答案1

awk替代能力相当有限。至少gawk可以gensub()在替换中包含匹配部分的一部分,但不能对这些部分进行任何操作。

可以使用awk,但您需要采取不同的方法:

awk '{
  text = $0
  $0 = ""
  while (match(text, /[0-9]+/)) {
    $0 = $0 substr(text, 1, RSTART-1) \
         (substr(text, RSTART, RLENGTH) - 1)
    text = substr(text, RSTART+RLENGTH)
  }
  $0 = $0 text
  print}'

或者使用 GNUawk作为 @jofel 方法的变体:

gawk -v 'RS=[0-9]+' '{printf "%s", $0 (RT==""?"":RT-1)}'

或者

gawk -v 'RS=[^0-9]+' '{printf "%s",($0==""?"":$0 - 1)RT}'

然而,这里更容易使用perl

perl -pe 's/\d+/$&-1/ge'

perl可以使用捕获组(如$1$2... 以及$&整个匹配部分),并且使用该e标志可以使用这些捕获组运行任意perl表达式。

答案2

您的 awk 解决方案仅匹配第一个数字,然后将所有其他数字替换为第一个数字减一。

就您的程序而言,您可以与 GNU 的 awk ( gawk) 一起使用:

awk 'BEGIN { RS="[^0-9]"; OFS=""; ORS=""; } {a=gensub(/([0-9]+)/,"\\1","g",$0);if(a~/[0-9]+/) {gsub(/[0-9]+/,(a-1),$0);} print $0,RT}'

但这可以简化为

awk 'BEGIN { RS="[^0-9]"; OFS=""; ORS=""; } {if(length($0)) {print ($0-1);}print RT}' 

或者加上评论:

awk '
  BEGIN { 
    RS="[^0-9]";  # set the record separator to a regexp matching all 
    OFS="";  # no output field separator
    ORS="";  # no output record separator (we use RT)
 } 
 {
     if(length($0)) { # if number found
       print ($0-1); # print it decreased by one
     }
     print RT # output current field separator (=non-digit). 
 }'

每个非数字用作记录分隔符并与打印语句一起重新插入。

这里有一个Python的解决方案:

python -c 'import re,sys; print re.compile("\d+").sub(lambda i: str(int(i.group())-1),sys.stdin.read()),' 

答案3

使用通用(非 GNU)“awk”:

我建议将输入行分成值和分隔符的数组。然后,修改值并用分隔符重新组合它们:

awk '{
    split("0," $0 ",0", numbers, "[^0-9]+"); # make sure each line starts and ends with a number
    split($0, sep, "[0-9]+");
    res = ""; j = 1;
    for (i = 2; i < length(numbers); i ++) { # ignore the dummy numbers added above
        res = res sep[j++] (numbers[i] - 1);
    }
    print res;
}' file

相关内容