Filtrar números que têm dígitos sequenciais, consecutivos ou não consecutivos

2

Eu tenho um arquivo cheio de números, número por linha. Cada número consiste em dois ou três dígitos.

Eu gostaria de filtrar esse arquivo por qualquer número com dois ou mais dígitos seqüenciais. Estes dígitos sequenciais podem ser consecutivos (por exemplo, 127, 215, 781), ou não consecutivos (por exemplo, 506). A ordem dos dígitos seqüenciais não é importante. Pode ser pequeno a grande (por exemplo, 127) ou grande a pequeno (por exemplo, 215).

Por exemplo:

127
215
781
874
370
01
10
142
506
94

A saída esperada:

370
94

Porque:

127 # Has two sequential and consecutive digits (1 and 2)
215 # Has two sequential and consecutive digits (1 and 2)
781 # Has two sequential and consecutive digits (7 and 8)
874 # Has two sequential and consecutive digits (7 and 8)
370 # Keep
01  # Has two sequential and consecutive digits (0 and 1)
10  # Has two sequential and consecutive digits (0 and 1)
142 # Has two sequential and non-consecutive digits (1 and 2)
506 # Has two sequential and non-consecutive digits (5 and 6)
94  # Keep
    
por Ahmed 22.09.2018 / 20:37

3 respostas

8

Com awk e definindo FS para string vazia (o efeito de usar FS vazio é um comportamento indefinido por POSIX e dependendo de qual versão awk você está usando, pode ser resultado diferente). Abaixo é testado no GNU awk :

awk -F '' '{ is_sequential=0;
    for (i=2; i<=NF; i++) { is_sequential+=($0 ~ $i-1 || $0 ~ $i+1)?1:0 }; }
    !is_sequential{ print }' infile

estamos verificando cada número $i para um número igual ao número-1 $i-1 ou número + 1 $i+1 em relação à linha inteira, o que significa que, se houver um número number-1 ou number+1 ou ambos vistos em uma linha, então descobrimos que há pelo menos dois números próximos um do outro (o primeiro, o número $i em si e o próximo $i-1 ou $i+1 ou ambos (sequencial) e com Condição ternária irá incrementar o valor da variável é_sequencial caso contrário sempre será 0.

No próximo bloco is_sequential{ print } , imprimimos a linha onde o valor está inalterado (o valor ainda é 0 , não há pelo menos dois números vistos que sejam sequenciais).

    
por 23.09.2018 / 19:03
2

Você pode tentar

awk '
  {split ("", N)                    # delete array N
    L = 1                           # initialise boolean L to TRUE
    for (i=1; i<=length($1); i++){  # for each digit
      P = substr($1, i, 1)                   
      if (N[P-1] || N[P+1]){        # if contiguous digit exists,
        L = 0          
        break                       # set L to FALSE; and quit the for loop
      }
      N[P] = 1
    } 
  }
  L
' file

Saída:

370
94

ou

awk '
  {split ("", N)
    L = 1
    for (i=1; i<=length; i++)
      N[substr($0,i,1)] = 1      # set all N elements for the digits in string

    for (i=0; i<9; i++)
      if (N[i] + N[i+1] == 2) {  # check for two adjacent elements to be TRUE
        L = 0          
        break
      }
  }
L
' file

Saída:

370
94

Testado no Ubuntu 18.04

    
por 23.09.2018 / 19:32
1

Aqui, como a lista de combinações é relativamente pequena, você pode considerá-las todas em uma alternância ERE:

grep -vE '0.*1|1.*[02]|2.*[13]|3.*[24]|4.*[35]|5.*[46]|6.*[57]|7.*[68]|8.*[79]|9.*8'

O mesmo com perl , mas usando o código perl em (??{...}) dentro do regexp para corresponder ao próximo dígito ou ao anterior:

perl -ne 'print unless /([0-8]).*(??{$1+1})/ || /([1-9]).*(??{$1-1})/'

Com o sed, você pode acrescentar a lista de pares consecutivos ao espaço padrão e usar referências posteriores para encontrar as correspondências:

sed -ne '1{x;s/$/0123456789876543210/;x;}' -e 'G;/\(.\).*\(.\).*\n.*/!P'
    
por 30.09.2018 / 10:19