Remova apenas as vírgulas presentes entre os números entre aspas duplas em um arquivo csv

Remova apenas as vírgulas presentes entre os números entre aspas duplas em um arquivo csv

Em um arquivo de texto, quero remover ,(vírgulas) e também as "(aspas) (somente se as aspas duplas contiverem números separados por vírgulas).

56,72,"12,34,54",x,y,"foo,a,b,bar"

Resultado esperado

56,72,123454,x,y,"foo,a,b,bar"

Observação:Mostro a linha acima apenas como exemplo. Meu arquivo de texto contém muitas linhas como acima e os números separados por vírgulas presentes entre aspas duplas devem variar. Aquilo é,

56,72,"12,34,54",x,y,"foo,a,b,bar"
56,92,"12,34",x,y,"foo,a,b,bar"
56,72,"12,34,54,78,76,54,67",x,y,"foo,a,b,bar"
56,72,x,y,"foo,a,b,bar","12,34,54"
56,72,x,y,"foo,a,b,bar","12,34,54","45,57,84,92","bar,foo"

Resultado esperado:

56,72,123454,x,y,"foo,a,b,bar"
56,92,1234,x,y,"foo,a,b,bar"
56,72,12345478765467,x,y,"foo,a,b,bar"
56,72,x,y,"foo,a,b,bar",123454
56,72,x,y,"foo,a,b,bar",123454,45578492,"bar,foo"

nvários números presentes entre aspas duplas separados por vírgulas. E também deixe as aspas duplas que contêm caracteres como estão.

Eu adoro sedferramenta de processamento de texto. Fico feliz se você postar alguma sedsolução para isso.

Responder1

Se perl estiver OK, aqui está uma maneira curta (e provavelmente rápida, se não necessariamente simples :)) de fazer isso:

perl -pe 's:"(\d[\d,]+)":$1=~y/,//dr:eg' file

O esinalizador para o s:::operador (que é apenas outra forma de escrever s///) faz com que a substituição seja tratada como uma expressão que é avaliada todas as vezes. Essa expressão pega a $1captura da regex (que já está sem as aspas) e a traduz ( y///, que também pode ser escrita como tr///) excluindo ( /d) todas as vírgulas. O rsinalizador to yé necessário para que o valor seja a string traduzida, em vez da contagem de traduções.

Para aqueles que de alguma forma se sentem manchados pelo perl, aqui está o equivalente em python. Python não é realmente uma ferramenta shell de uma linha, mas às vezes pode ser persuadido a cooperar. O seguinte pode ser escrito como uma linha (ao contrário fordos loops, que não podem ser), mas a rolagem horizontal torna-o (ainda mais) ilegível:

python -c '
import re;
import sys;
r=re.compile("\"(\d+(,\d+)*)\"");
all(not sys.stdout.write(r.sub(lambda m:m.group(1).replace(",",""),l))
    for l in sys.stdin)
' < file

Responder2

Isto (adaptado deaqui) deve fazer o que você precisa, embora o Perl do @rici seja muito mais simples:

$ sed -r ':a;s/(("[0-9,]*",?)*"[0-9,]*),/\1/;ta; s/""/","/g; 
          s/"([0-9]*)",?/\1,/g ' file
56,72,123454,x,y,"foo,a,b,bar"
56,92,1234,x,y,"foo,a,b,bar"
56,72,12345478765467,x,y,"foo,a,b,bar"
56,72,x,y,"foo,a,b,bar",123454,
56,72,x,y,"foo,a,b,bar",123454,45578492,"bar,foo"

Explicação

  • :a: defina um rótulo chamado a.
  • s/(("[0-9,]*",?)*"[0-9,]*),/\1/: Este precisa ser dividido
    • Em primeiro lugar, usando esta construção : (foo(bar)), \1será foobare \2será bar.
    • "[0-9,]*",?: corresponde a 0 ou mais de 0-9ou ,, seguido por 0 ou 1 ,.
    • ("[0-9,]*",?)*: corresponde a 0 ou mais dos itens acima.
    • "[0-9,]*: corresponde a 0 ou mais de 0-9ou ,que vem logo após um"
  • ta;: volte para o rótulo ae execute novamentesea substituição foi bem sucedida.
  • s/""/","/g;: pós-processamento. Substituir ""com ",".
  • s/"([0-9]*)",?/\1,/g: remova todas as aspas em torno dos números.

Isso pode ser mais fácil de entender com outro exemplo:

$ echo '"1,2,3,4"' | sed -nr ':a;s/(("[0-9,]*",?)*"[0-9,]*),/\1/;p;ta;'
"1,2,34"
"1,234"
"1234"
"1234"

Portanto, embora você possa encontrar um número logo após uma aspa e seguido de uma vírgula e outro número, junte os dois números e repita o processo até que não seja mais possível.

Neste ponto, acredito que seja útil mencionar uma citação que info sedaparece na seção que descreve funções avançadas, como o rótulo usado acima (obrigado por encontrar if @Braiam):

Na maioria dos casos, o uso desses comandos indica que provavelmente será melhor programar em algo como `awk' ou Perl.

Responder3

Para dados CSV, eu usaria uma linguagem com um analisador CSV real. Por exemplo com Ruby:

ruby -rcsv -pe '
  row = CSV::parse_line($_).map {|e| e.delete!(",") if e =~ /^[\d,]+$/; e} 
  $_  = CSV::generate_line(row)
' <<END
56,72,"12,34,54",x,y,"foo,a,b,bar"
56,92,"12,34",x,y,"foo,a,b,bar"
56,72,"12,34,54,78,76,54,67",x,y,"foo,a,b,bar"
56,72,x,y,"foo,a,b,bar","12,34,54"
56,72,x,y,"foo,a,b,bar","12,34,54","45,57,84,92","bar,foo"
END
56,72,123454,x,y,"foo,a,b,bar"
56,92,1234,x,y,"foo,a,b,bar"
56,72,12345478765467,x,y,"foo,a,b,bar"
56,72,x,y,"foo,a,b,bar",123454
56,72,x,y,"foo,a,b,bar",123454,45578492,"bar,foo"

Responder4

UsandoRaku(anteriormente conhecido como Perl_6)

~$ raku -pe 's:g/ \" ~ \" (\d+) ** 2..* % "," /{$0.join}/;'  file

Entrada de amostra:

56,72,"12,34,54",x,y,"foo,a,b,bar"
56,92,"12,34",x,y,"foo,a,b,bar"
56,72,"12,34,54,78,76,54,67",x,y,"foo,a,b,bar"
56,72,x,y,"foo,a,b,bar","12,34,54"
56,72,x,y,"foo,a,b,bar","12,34,54","45,57,84,92","bar,foo"

Saída de amostra:

56,72,123454,x,y,"foo,a,b,bar"
56,92,1234,x,y,"foo,a,b,bar"
56,72,12345478765467,x,y,"foo,a,b,bar"
56,72,x,y,"foo,a,b,bar",123454
56,72,x,y,"foo,a,b,bar",123454,45578492,"bar,foo"

Raku é uma linguagem de programação da família Perl com vários recursos regex poderosos. Veja o URL abaixo para uma visão geral desta resposta:

https://unix.stackexchange.com/a/722570/227738

No código acima, os dígitos são reconhecidos e as vírgulas incorporadas são removidas. A regex aproveita o fato de queestruturas aninhadaspode ser denotado com a nova notação ~ til (aninhada) de Raku, que \" ~ \" [\d+]significa "um ou mais dígito(s) cercado(s) por "aspas duplas".

Adicionalmente,estruturas repetidaspode ser denotado com o novo quantificador modificado de Raku %para estruturas repetidas. A notação [\d+] ** 2..* % "," significa "um ou mais dígitos separados por ,vírgulas com este padrão repetido ** 2..*duas ou mais vezes. [Se houver um separador final (por exemplo vírgula), use a %%em vez de %na sintaxe].

Isto é só o começo. Arquivos CSV com separadores alternativos, novas linhas incorporadas, vírgulas incorporadas, campos potencialmente em branco, etc., realmente precisam ser tratados por um verdadeiro analisador CSV como o Text::CSVmódulo de Raku. Veja os links abaixo para obter detalhes.

https://docs.raku.org/linguagem/regexes
https://raku.land/github:Tux/Text::CSV
https://raku.org

informação relacionada