Implementando um regexp estendido para adicionar um número variável de zeros à esquerda com base na posição em uma string

10

Estou tendo problemas para obter minha sintaxe sed para adicionar um número variável de zeros à esquerda em um esquema organizacional numérico. As strings que estou operando aparecem como

1.1.1.1,Some Text Here

aproveitando a sintaxe do sed

sed -r ":r;s/\b[0-9]{1,$((1))}\b/0&/g;tr"

Eu sou capaz de obter a resposta

01.01.01.01,Some Text Here

No entanto, o que estou procurando é algo para preencher até zero de 2 dígitos nos campos 2 e 3 e 3 dígitos no campo 4, para que todos os itens tenham um tamanho padrão de [0-9]. [0- 9] {2}. [0-9] {2}. [0-9] {3}

1.01.01.001,Some Text Here

Para a vida de mim eu não consigo entender nem mesmo como modificar o limite para incluir os parâmetros necessários para encaixar em apenas numerais após um período. Eu acho que tem algo a ver com o uso do \ b que eu entendo combina com zero caracteres em um limite de palavra, mas eu não entendo porque minhas tentativas de adicionar um período para a partida falhar da seguinte forma:

sed -r ":r;s/\.\b[0-9]{1,$((1))}\b/0&/g;tr"
sed -r ":r;s/\b\.[0-9]{1,$((1))}\b/0&/g;tr"
Both cause the statement to hang

sed -r ":r;s/\b[0-9]\.{1,$((1))}\b/0&/g;tr"
sed -r ":r;s/\b[0-9]{1,$((1))}\.\b/0&/g;tr"
sed -r ":r;s/\b[0-9]{1,$((1))}\b\./0&/g;tr"
cause the statement to output:

1.01.01.1,Some Text Here

Além disso, espero ter problemas adicionais se a instrução contiver texto como:

1.1.1.1,Some Number 1 Here

É uma conclusão precipitada que preciso realmente aprender sed e todas as suas complexidades. Estou trabalhando nisso, mas espero que esta declaração particular continue a me causar problemas por um tempo. Qualquer ajuda seria muito apreciada.

EDIT: Eu descobri uma maneira ... Esta declaração parece fazer o que eu estou procurando, mas tem que haver uma maneira mais elegante para fazer isso.

sed -r ':r;s/\b[0-9]{1,1}\.\b/0&/;tr;:i;s/\b[0-9]{1,2},\b/0&/;ti;s/.//'

Além disso, sintaticamente, isso causará problemas se um formato numérico semelhante aparecer no texto ... semelhante a:

1.1.1.1,Some Text Referring to Document XXX Heading 1.2.3

Nesse caso, isso resultará em:

1.01.01.001,Some Text Referring to Document XXX Heading 01.02.03

Resolvido Obrigado a todos pela ajuda aqui. Inicialmente resolvi o problema com a resposta que aceitei abaixo. Eu senti que a solução foi transferida para o Python como parte de uma solução maior, aproveitando o tipo abaixo:

def getPaddedKey(line):
    keyparts = line[0].split(".")
    keyparts = map(lambda x: x.rjust(5, '0'), keyparts)
    return '.'.join(keyparts)

s=sorted(reader, key=getPaddedKey)
    
por daijizai 18.07.2017 / 18:42

5 respostas

4

Uso: leading_zero.sh input.txt

#!/bin/bash

sed -r '
    s/\.([0-9]{1,2})\.([0-9]{1,2})\.([0-9]{1,3},)/.0.0.00/
    s/\.0*([0-9]{2})\.0*([0-9]{2})\.0*([0-9]{3})/.../
' "$1"

Explicação:

  1. Primeira legenda, adicione uma certa quantidade de zeros a cada número. 1 zero a 2 e 3 números, 2 zero a 4 números. Não importa quantos dígitos já existem.
  2. A segunda substituição remove todos os zeros extras, deixando apenas a quantidade necessária de números. 2 e 3 números devem conter apenas 2 dígitos. Deixa-os e remove os restos. O quarto número deve conter apenas 3 dígitos. Deixa-os e remove os restos.

input.txt

1.1.1.1,Some Text Here
1.1.1.1,Some Text Here
1.11.1.11,Some Text Referring to Document XXX Heading 1.2.3
1.1.1.1,Some Text Here
1.1.11.111,Some Text Referring to Document XXX Heading 1.2.3
1.11.1.1,Some Text Here

output.txt

1.01.01.001,Some Text Here
1.01.01.001,Some Text Here
1.11.01.011,Some Text Referring to Document XXX Heading 1.2.3
1.01.01.001,Some Text Here
1.01.11.111,Some Text Referring to Document XXX Heading 1.2.3
1.11.01.001,Some Text Here
    
por 18.07.2017 / 23:07
9
O

bash pode lidar com isso. Vai ser muito mais lento que o perl:

echo "1.1.1.1,Some Text Here" | 
while IFS=., read -r a b c d text; do
    printf "%d.%02d.%02d.%03d,%s\n" "$a" "$b" "$c" "$d" "$text"
done
1.01.01.001,Some Text Here
    
por 18.07.2017 / 21:31
5

Você não solicitou especificamente uma solução perl , mas uma delas é assim mesmo. Pessoalmente acho que é um pouco mais fácil de ler, especialmente quando dividido em várias linhas.

Primeiro aqui é o one-liner:

(
    echo '1.2.3.4,Some Text Here'
    echo '1.01.01.1,Some Text Here'
    echo '1.1.1.1,Some Number 1 Here'
    echo '1.1.1.1,Some Text Referring to Document XXX Heading 1.2.3'
    echo '1.2.3.4,Some \n \s \text'
) |
perl -ne '($ip, $text) = split(/,/, $_, 2); $ip = sprintf("%1d.%02d.%03d.%03d", split(/\./, $ip)); print "$ip,$text"'

Seus resultados:

1.02.003.004,Some Text Here
1.01.001.001,Some Text Here
1.01.001.001,Some Number 1 Here
1.01.001.001,Some Text Referring to Document XXX Heading 1.2.3
1.02.003.004,Some \n \s \text

E aqui está o script perl quebrado e comentado (o sinal -n coloca um loop implícito de while read; do ... done ao redor do código):

($ip, $text) = split(/,/, $_, 2);                # Split line into two parts by comma
@octets = split(/\./, $ip)                       # Split IP address into octets by dots
$ip = sprintf("%1d.%02d.%03d.%03d", @octets);    # Apply the formatting
print "$ip,$text"                                # Output the two parts
    
por 18.07.2017 / 20:43
3

Aqui está uma abordagem possível:
sed -E 's/([0-9]*\.)/0/g;s/.//;s/([0-9]*,)/00/'

Exemplos

echo "1.11.111.1111,Some Text Here" | sed -E 's/([0-9]*\.)/0/g;s/.//;s/([0-9]*,)/00/'
1.011.0111.001111,Some Text Here

Trabalhe também com essa string:

echo "1.1.1.1,Some Number 1 Here" | sed -E 's/([0-9]\.)/0/g;s/.//;s/([0-9],)/00/'
1.01.01.001,Some Number 1 Here

... e esta string:

echo "1.2.2101.7191,Some Text Here" | sed -E 's/([0-9]*\.)/0/g;s/.//;s/([0-9]*,)/00/'
1.02.02101.007191,Some Text Here
    
por 18.07.2017 / 19:16
2
perl -pe '/^\d/g && s/\G(?:(\.\K\d+(?=\.))|\.\K\d+(?=,))/sprintf "%0".($1?2:3)."d",$&/ge'

Explicação:

O método usado aqui é observar as vizinhanças dos números e agir com base nisso. Assim, os números 2 e 3 vêem um ponto em ambos os lados, enquanto o 4º numérico vê ponto à esquerda e uma vírgula à direita.

O $ 1 é definido quando o regex toma o caminho do 2º ou 3º dígito e, consequentemente, o preenchimento de precisão é 2. OTOH, para o 4º num, o preenchimento é 3.

% cat file.txt

1.00.3.4,Some Text Here
1.01.01.1,Some Text Here
1.0.01.1,Some Number 1 Here
1.1.1.1,Some Text Referring to Document XXX Heading 1.2.3.4
1.2.3.4,Some \n \s \text

Resultados:

1.00.03.004,Some Text Here
1.01.01.001,Some Text Here
1.00.01.001,Some Number 1 Here
1.01.01.001,Some Text Referring to Document XXX Heading 1.2.3.4
1.02.03.004,Some \n \s \text
    
por 19.07.2017 / 08:37