Remove apenas as vírgulas presentes entre aspas duplas

10

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

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

Saída esperada

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

Nota: mostro a linha acima apenas como um 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. Isto é,

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"

Existe um número% de númerosn presentes nas aspas duplas separadas por vírgulas. E também deixe as aspas duplas que contém os caracteres como estão.

Adoro a ferramenta de processamento de texto sed . Fico feliz se você postar qualquer solução sed para isso.

    
por Avinash Raj 12.05.2014 / 05:13

4 respostas

7

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

$ sed -r ':a;s/(("[0-9,]*",?)*"[0-9,]*),//;ta; s/""/","/g; 
          s/"([0-9]*)",?/,/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 : define um rótulo chamado a .
  • s/(("[0-9,]*",?)*"[0-9,]*),// : Este precisa ser dividido
    • Antes de mais nada, use esta construção: (foo(bar)) , será foobar e será bar .
    • "[0-9,]*",? : corresponde a 0 ou mais de 0-9 ou , , seguido por 0 ou 1 , .
    • ("[0-9,]*",?)* : corresponde a 0 ou mais dos acima.
    • "[0-9,]* : corresponde a 0 ou mais de 0-9 ou , que aparecem imediatamente após "
  • ta; : volte para o rótulo a e execute novamente se a substituição foi bem sucedida.
  • s/""/","/g; : pós-processamento. Substitua "" por "," .
  • s/"([0-9]*)",?/,/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,]*),//;p;ta;'
"1,2,34"
"1,234"
"1234"
"1234"

Assim, enquanto você pode encontrar um número logo após uma citação e seguido por uma vírgula e outro número, junte os dois números juntos e repita o processo até que não seja mais possível.

Neste ponto, acredito que seja útil mencionar uma citação de info sed que aparece na seção descrevendo funções avançadas, como o rótulo usado acima (obrigado por descobrir se @Braiam):

In most cases, use of these commands indicates that you are probably better off programming in something like 'awk' or Perl.

    
por 12.05.2014 / 05:27
10

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

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

O e flag para o operador s::: (que é apenas outra maneira de escrever s/// ) faz com que a substituição seja tratada como uma expressão que é avaliada sempre. Essa expressão pega a $1 capture da regex (que já está faltando as aspas) e traduz ( y/// , que também pode ser escrito como tr/// ) excluindo ( /d ) todas as vírgulas. O sinal r 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 maculados pelo perl, aqui está o equivalente python. O Python realmente não é uma ferramenta de shell one-liner, mas às vezes ele pode ser convencido a cooperar. O seguinte pode ser escrito como uma linha (ao contrário de for loops, que não pode ser), mas a rolagem horizontal torna (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
    
por 12.05.2014 / 06:59
6

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"
    
por 12.05.2014 / 17:53
0

Blockquote

Olá, aqui está o código Python para substituir as vírgulas por aspas duplas, vírgulas são substituídas pelo caractere pipe (|)

Este código Python é para substituir vírgulas entre aspas duplas

por exemplo: x, y, z, 1,2, "r, e, t, y", h, 8,5,6

se substituir por Pipe x, y, z, 1,2, "r | e | t | y", h, 8,5,6

se substituir com nulo x, y, z, 1,2, "rety", h, 8,5,6

writingFile = open('FileToWrite', 'w')
with open('FileToRead') as f:

    while True:

        c = f.read(1)
        if not c:
            print ("End of file")
            break
        print ("Read a character:", c)


        if c=='"':
            writingFile.write(c) 
            c = f.read(1)
            while c != '"':
                if c== ',':
                    c= '|'
                writingFile.write(c)
                c = f.read(1)


        writingFile.write(c)


writingFile.close()
    
por 29.12.2016 / 11:42