Transformando arquivo CSV usando sed

7

Para poder importar alguns dados para uma determinada ferramenta, tenho que transformar um arquivo CSV desse formato

"data","data","data data","data","123"

para este formato

data;data;data data;data;123

As colunas nunca contêm " , ; ou , , mas podem haver espaços. Atualmente eu uso o seguinte

sed -e 's/","/;/g' -e 's/"//g' input.csv > output.csv

Embora isso funcione bem, eu me pergunto se isso pode ser feito de maneira mais elegante, ou seja,

  • O sed é a ferramenta certa (padrão Unix) para o trabalho?
  • Seria possível mesclar as duas expressões em uma só?

Obrigado pela sua contribuição!

    
por middus 10.10.2009 / 16:46

3 respostas

6
( tr , ';' | tr -d '"' ) < input.csv > output.csv

Eu usaria o Perl

perl -pe 'tr/,"/;/d' input.csv > output.csv

- mas esta tarefa específica não está além do sed. Você não pode mesclar as duas expressões.

    
por 10.10.2009 / 17:12
5

Qual você prefere (perl, sed, awk) é com você; todos eles farão o trabalho. Desde que você pediu sed e os outros são postados, aqui vai você. Esta é uma forma mais simples do seu regex e funciona com sua linha de exemplo:

$ sed -e 's/"//g; s/,/;/g' infile.csv > outfile.csv

Note que pode juntar as duas expressões com um ponto e vírgula após cada substituição. Testado com o GNU sed v4.1.5.

Aqui estão suas expressões originais associadas:

$ sed -e 's/","/;/g; s/"//g' infile.csv > outfile.csv

Tenho certeza de que é possível mesclar as duas substituições. Não tenho certeza do que seria improvável, e tenho certeza de que o resultado será muito menos legível do que o script no topo. Se eu inventar alguma coisa (ou alguém pesar nos comentários) eu adicionarei aqui.

    
por 10.10.2009 / 18:54
4

Como você lida com registros, awk faz mais sentido. Dito isso, não é muito bom em CSV, já que os delimitadores de campo são um pouco variáveis. Mas se você tiver certeza de que todos os campos estão cercados por cota dupla, isso funcionará:

awk -F'","' 'BEGIN {OFS=";"} { gsub(/(^")|("$)/, ""); $1=$1; print }'

Isso define o separador de campo de entrada do awk como " "," " (incluindo o conjunto interno de doublequotes). Isso quase funciona, exceto pelo fato de você precisar lidar com as cotações iniciais e finais, que são removidas com a função gsub . O $1=$1 força-o a recompilar o registro com o novo separador de campo de saída, que foi definido como ; no bloco BEGIN. Então, print imprime todo o registro.

Isso é um pouco mais organizado:

awk -F '(^")|(",")|("$)' 'BEGIN {OFS=";"} { $1=$1; print }'

Ele define o separador de campo de entrada como uma expressão regular que inclui aspas duplas no início e no final do registro, mas também faz com que ele imprima um campo vazio inicial e final. Você pode facilmente se livrar do campo à direita:

awk -F '(^")|(",")|("$)' 'BEGIN {OFS=";"} { NF=NF-1; $1=$1; print }'

NF é o número de campos e reduzi-lo em um deles, saindo do último campo. Mas não consigo pensar em uma maneira de cortar o primeiro campo.

Se você sabe que a entrada sempre tem cinco campos, você pode fazer isso:

awk -F '(^")|(",")|("$)' 'BEGIN {OFS=";"} { print $2,$3,$4,$5,$6 }'

Observe que isso elimina a construção $1=$1 , que só precisamos se estivermos imprimindo o (implícito) $ 0.

Tudo o que foi dito, eu provavelmente acabaria usando o perl e um dos muitos módulos CSV disponíveis no CPAN .

    
por 10.10.2009 / 18:38

Tags