Substitua cada vírgula fora das aspas duplas por um pipe

5

Usando sed, eu gostaria de substituir cada vírgula que está fora das aspas duplas por um pipe.

Para que esta linha no arquivo .csv:

John,Tonny,"345.3435,23",56th Street

Seria convertido para:

John|Tonny|"345.3435,23"|56th Street

Você poderia me ajudar com o regex para isso?

    
por Quentin 20.06.2018 / 10:43

5 respostas

10

Se o seu sed suportar a opção -E ( -r em algumas implementações):

sed -Ee :1 -e 's/^(([^",]|"[^"]*")*),/|/;t1' < file

O

:label
   s/pattern/replacement/
t label

É um idioma sed muito comum. Ele continua fazendo a mesma substituição em um loop, desde que seja bem-sucedido.

Aqui, substituímos a parte principal da linha de 0 ou mais strings ou caracteres citados, que são " e , (capturados em ) seguidos por , com de captura e | , portanto, em sua amostra, isso significa:

  • John,Tonny,"345.3435,23",56th Street - > %código%
  • John|Tonny,"345.3435,23",56th Street - > %código%
  • John|Tonny,"345.3435,23",56th Street - > %código%
  • e paramos aqui porque o padrão não combina mais com isso.

Com John|Tonny|"345.3435,23",56th Street , você poderia fazer isso com uma substituição com o sinal John|Tonny|"345.3435,23",56th Street :

perl -pe 's{("[^"]*"|[^",]+)|,}{$1 // "|"}ge'

Aqui, assumindo que aspas são balanceadas na entrada, o padrão corresponderia a todas as entradas, dividindo-as em:

  • string entre aspas
  • sequências de caracteres diferentes de John|Tonny|"345.3435,23"|56th Street ou perl
  • uma vírgula

E somente quando a string correspondente for uma vírgula (quando g não estiver definido na peça de substituição), substitua-a por , .

    
por 20.06.2018 / 10:48
14

Usando csvkit :

$ csvformat -D '|' file.csv
John|Tonny|345.3435,23|56th Street

As ferramentas no csvkit sabem como lidar com as complexidades dos arquivos CVS, e aqui estamos usando csvformat para substituir as vírgulas de delimitação por | corretamente. Os campos de saída serão citados conforme necessário.

Exemplo:

$ cat file.csv
John,Tonny,"345.3435,23",56th Street
The | factory,Ltd.,"0,0",meep meep

$ csvformat -D '|' file.csv
John|Tonny|345.3435,23|56th Street
"The | factory"|Ltd.|0,0|meep meep
    
por 20.06.2018 / 11:02
3

com perl

perl -MText::CSV -lne '
  BEGIN { $p = Text::CSV->new() } 
  print join "|", $p->fields() if $p->parse($_)
' file.csv
John|Tonny|345.3435,23|56th Street
    
por 20.06.2018 / 13:25
0
gawk '{gsub(/,/,"|"); printf("%s", $0 RT)}' RS='"[^"]*"' input.txt

ou o mesmo, mas de outra forma:

gawk -F, '{$1=$1; printf("%s", $0 RT)}' RS='"[^"]*"' OFS='|' input.txt

Entrada

John,Tonny,"345.3435,23",56th Street
one,"two,three",four,five,"six,stub,seven",eight,nine
one,"two,three",four,five,"six,stub,seven",eight,nine,"ten,eleven,twelve"

Resultado

John|Tonny|"345.3435,23"|56th Street
one|"two,three"|four|five|"six,stub,seven"|eight|nine
one|"two,three"|four|five|"six,stub,seven"|eight|nine|"ten,eleven,twelve"
    
por 21.06.2018 / 14:43
0

Com Python e csv module:

import csv,sys

with open(sys.argv[1]) as csvfile:
    csvr = csv.reader(csvfile)
    for line in csvr:
        dup = map( lambda x: '"' + x + '"' if ',' in x else x, line )
        print('|'.join(dup))

Funciona da seguinte forma:

$ python3 csvfile.py  input.csv 
John|Tonny|"345.3435,23"|56th Street
John|Doe|"123.456,25"|26th Street
Jane|Doe|"987.654,52"|15th Street
    
por 26.07.2018 / 07:42