O que significa o seguinte código sed de processamento de números?

-1

Me deparei com um código no script que adiciona ',' após cada 3 dígitos de trás para frente. o código considera apenas dados numéricos.

A seguir está o código.

sed 's/\(^\|[^0-9.]\)\([0-9]\+\)\([0-9]\{3\}\)/\1\2,\3/g' number.txt

number.txt

1234
12345
123456

resultado

1,234
12,345
123,456
1234,567

Alguém pode explicar o fluxo de código ..

Couve de Dhaval
fonte
1
Você parece saber o que o código faz; não há ramificação e apenas um comando. De onde vem seu mal-entendido?
Jeff Schaller

Respostas:

3

sed (o s tream ed itor) pode operar em s earch e modo de substituir usando expressões regulares . Há um pouco de escape específico do sed, mas para o próprio regex, você pode conferir a ferramenta de explicação do regexr :

( Grupo de captura nº 1. Agrupa vários tokens e cria um grupo de captura para extrair uma substring ou usar uma referência anterior.

^ Começando. Corresponde ao início da string.

| Alternação. Atua como um OR booleano. Corresponde à expressão antes ou depois do |.

[^ Conjunto negado. Corresponde a qualquer caractere que não esteja no conjunto.

0-9 Alcance. Corresponde a um caractere no intervalo "0" a "9". Maiúsculas e Minúsculas.

. Personagem. Corresponde a um "." personagem.

]

)

( Grupo de captura nº 2. Agrupa vários tokens e cria um grupo de captura para extrair uma substring ou usar uma referência anterior.

[ Conjunto de caracteres. Corresponde a qualquer caractere no conjunto.

0-9 Alcance. Corresponde a um caractere no intervalo "0" a "9". Maiúsculas e Minúsculas.

]

+ Quantificador. Combine 1 ou mais do token anterior.

)

( Grupo de captura # 3. Agrupa vários tokens e cria um grupo de captura para extrair uma substring ou usar uma referência anterior.

[ Conjunto de caracteres. Corresponde a qualquer caractere no conjunto.

0-9 Alcance. Corresponde a um caractere no intervalo "0" a "9". Maiúsculas e Minúsculas.

]

{3} Quantificador. Combine 3 do token anterior.

)

Hermann
fonte
1
... " 0-9 Range. Matches a character in the range "0" to "9". Case sensitive"; Por Case sensitive, você quer dizer dígitos Inglês? ou?
αғsнιη 26/03
Não, diferencia maiúsculas de minúsculas significa que letras grandes e pequenas fazem a diferença. As expressões regulares geralmente diferenciam maiúsculas de minúsculas (a menos que configuradas de outra forma, por exemplo, um comutador i ). Nesse caso específico, o conjunto de caracteres contém apenas dígitos. Eles têm variantes grandes e pequenas para que a distinção entre maiúsculas e minúsculas não importe. Esta é a saída dada pelo regexr - sinta-se à vontade para experimentá-lo. A ferramenta de explicação é bastante interativa.
Hermann
1

O padrão captura (1) o início da linha ou algo que não é um dígito nem um ponto, seguido por (2) qualquer número de dígitos, pelo menos um, seguido por (3) exatamente três dígitos. Em seguida, os coloca de volta com uma vírgula entre (2) e (3), com efeito adicionando um separador de milhar. O primeiro grupo é necessário apenas para evitar tocar em partes fracionárias após um ponto decimal, pois não queremos 1.2345nos transformar em 1.2,345.

Observe que o padrão é escrito em expressões regulares básicas (BRE), exigindo barras invertidas na frente de cada uma ()e {}para torná-las especiais. Além disso, requer GNU sed, onde \+e \|também tem significados especiais no BRE como uma extensão . O comando seria melhor escrito como uma expressão regular estendida ( sed -Eé suportada por muitas implementações sed):

sed -E 's/(^|[^0-9.])([0-9]+)([0-9]{3})/\1\2,\3/g'

Além disso, o padrão faz apenas uma substituição, não inclui vários milhares de separadores no mesmo número. O /gfinal corresponderá várias vezes na mesma linha, mas ainda não processa os dados substituídos. 1234567vai se tornar 1234,567, não 1,234,567. Para corrigir isso, precisamos adicionar um loop:

sed -E -e :a  -e 's/(^|[^0-9.])([0-9]+)([0-9]{3})/\1\2,\3/g' -e ta

Aqui, :aé apenas um rótulo, e as finais ta t ests para uma substituição bem-sucedida, e salta de volta para ase um substituto foi feito, na verdade repetindo o processo tantas vezes quanto ele faz algo. Daí 1234567vai se transformar 1,234,567.

ilkkachu
fonte
1
  • sed 's/foo/bar/g' number.txt: Lê o arquivo number.txte substitui o padrão regex foopor bar. Isso ocorrerá para todas as correspondências em cada linha ( /g).
  • \(^\|[^0-9.]\)\([0-9]\+\)\([0-9]\{3\}\): Este é o padrão para substituir. Cada parte entre parênteses escapados \(…\)é um "grupo de captura". O padrão interno é "capturado" para uso posterior.
    • \(^\|[^0-9.]\): Encontre o início da linha ^ou \|um caractere que não seja um número ou ponto [^0-9.]. Essencialmente, encontra o caractere que precede um número.
    • \([0-9]\+\): Encontre um ou mais números [0-9]\+.
    • \([0-9]\{3\}\): Encontre 3 numerais [0-9]\{3\}.
  • \1\2,\3: substitua as correspondências acima pelos dois primeiros grupos de captura, seguidos pelo ,último grupo de captura. Em outras palavras, insira ,entre o segundo e o terceiro padrão.

Por sedser "ganancioso", ele tentará maximizar a duração da partida. Portanto, o grupo de captura final será os três últimos números do número.

Nota: muitos dos caracteres "especiais" são escapados com \, por exemplo, \(…\)e \{3\}. Se suas sed"expressões regulares estendidas" suportadas com -Eou -r, você não precisaria escapar delas. Isso melhoraria a legibilidade.

Sparhawk
fonte