Como subtrair 1 dos números correspondentes à expressão regular?

5

Estou tentando escrever um script que reduz cada número na fila por "1", mas estou recebendo todos os "0" s:

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

Por exemplo, a string:

1,2,3,4-7

deve resultar em:

0,1,2,3-6

estou recebendo:

0,0,0,0-0
    
por Max Shepelev 22.04.2016 / 10:53

3 respostas

10
Os recursos de substituição

awk são bastante limitados. gawk tem gensub() que pode incluir pelo menos partes da porção casada na substituição, mas nenhuma operação pode ser feita nelas.

É possível com awk , mas você precisa adotar uma abordagem diferente:

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}'

Ou com o GNU awk como uma variação na abordagem do @jofel:

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

ou

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

No entanto, aqui é muito mais fácil com perl :

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

perl pode usar grupos de captura (como $1 , $2 ... e $& para toda a parte correspondida) e com o sinal e pode executar expressões perl arbitrárias com esses. / p>     

por 22.04.2016 / 11:33
7

Sua solução awk corresponde apenas ao primeiro número e, em seguida, substitui todos os outros números com o primeiro número reduzido em um.

Pegando seu programa, você pode usar com o awk do GNU ( 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}'

Mas isso pode ser simplificado para

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

Ou com comentários:

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). 
 }'

Cada dígito não é usado como separador de registro e reinserido com a declaração de impressão.

Aqui uma solução em python:

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

Usando genérico (não-GNU) "awk":

Eu sugeriria uma separação das linhas de entrada em matrizes de valores e separadores. Em seguida, modifique os valores e recombine-os com os separadores:

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
    
por 22.04.2016 / 11:38