Convertendo CSV para TSV

27

Eu tenho vários arquivos CSV grandes e gostaria deles em TSV (formato separado por tabulação). A complicação é que existem vírgulas nos campos do arquivo CSV, por exemplo:

 A,,C,"D,E,F","G",I,"K,L,M",Z

Saída esperada:

 A      C   D,E,F   G   I   K,L,M   Z

(onde os espaços em branco no meio são guias "difíceis")

Eu tenho Perl, Python e coreutils instalados neste servidor.

DarkHeart
fonte
Eu faria isso com o node.js ou com o perl.
peterh diz restabelecer Monica
1
Substitua vírgulas não cotados com guias ...
cricket_007
Sim, se eu tivesse mais de 5 minutos para esta pergunta. Mas terei prazer em apoiar os respondentes com meus votos. O que eu tentei dizer, que as coisas comuns de sed / awk provavelmente não são elegíveis para isso (pelo menos em seu uso comum).
peterh diz restabelecer Monica
6
Não sei se o seu exemplo é representativo dos dados reais, mas se eles serão cadeias de texto reais, não esqueça que talvez você precise lidar com o caso em que a cadeia inclui uma guia ...
AC
3
A outra parte complicada é que o CSV é um formato muito pouco definido, não existe um padrão real (existe uma RFC, mas foi escrita anos após o fato). Escrevi código que usava um analisador CSV fornecido pelo idioma e tive que reescrevê-lo com um analisador personalizado, porque achei que os dados de entrada estavam em uma variante quebrada do formato csv.
precisa saber é o seguinte

Respostas:

37

Python

Adicione ao arquivo nomeado csv2tab.she torne-o executável

#!/usr/bin/env python
import csv, sys
csv.writer(sys.stdout, dialect='excel-tab').writerows(csv.reader(sys.stdin))

Execuções de teste

$ echo 'A,,C,"D,E,F","G",I,"K,L,M",Z' | ./csv2tab.sh                         
A       C   D,E,F   G   I   K,L,M   Z

$ ./csv2tab.sh < data.csv > data.tsv && head data.tsv                                                   
1A      C   D,E,F   G   I   K,L,M   Z
2A      C   D,E,F   G   I   K,L,M   Z
3A      C   D,E,F   G   I   K,L,M   Z
cricket_007
fonte
5
Um possível bug: essa resposta não escapa às guias internas.
quer
4
@Morgen csv.writer(sys.stdout, dialect='excel-tab').writerows(csv.reader(sys.stdin))? Elimina o loop também.
Muni
1
@chx try python -c 'import csv,sys; csv.writer(sys.stdout, dialect="excel-tab").writerows(csv.reader(sys.stdin))'. Duvido -mque funciona dessa maneira.
Muni
18

Por diversão sed,.

sed -E 's/("([^"]*)")?,/\2\t/g' file

Se o seu sednão suportar -E, tente com -r. Se você sednão suporta \tuma guia literal, tente colocar uma guia literal (em muitos shells, ctrl- v tab) ou no Bash, use uma $'...'string no estilo C (nesse caso, a barra invertida \2precisará ser duplicada). Se você deseja manter as aspas, use em \1vez de \2(nesse caso, o par interno de parênteses é inútil e pode ser removido).

Isso não faz nenhuma tentativa de lidar com aspas duplas escapadas dentro de aspas duplas; alguns dialetos CSV suportam isso duplicando as aspas duplas (sic).

triplo
fonte
1
Eu acho que tentei cerca de 100 scripts sed diferentes para conseguir esse, mas todas as minhas tentativas falharam. Isso é incrível.
21417 George Vasiliou
16

Usando o csvkitutilitário (Python), por exemplo:

$ csvformat -T in.csv > out.txt

Faz streaming, com CSV e TSV correto, citando e escapando

Está no apt e em outros gerenciadores de pacotes

Neil McGuigan
fonte
13

Uma opção pode ser o módulo Text :: CSV do perl, por exemplo

perl -MText::CSV -lne 'BEGIN { $csv = Text::CSV->new() }
  print join "\t", $csv->fields() if $csv->parse($_)
' somefile

para demonstrar

echo 'A,,C,"D,E,F","G",I,"K,L,M",Z' |
  perl -MText::CSV -lne 'BEGIN { $csv = Text::CSV->new() }
  print join "\t", $csv->fields() if $csv->parse($_)
'
A       C   D,E,F   G   I   K,L,M   Z
chave de aço
fonte
1
Não seria correto se um campo contém uma guia
Neil McGuigan
6

Perl

perl -lne '
   my $re = qr/,(?=(?:[^"]*"[^"]*")*(?![^"]*"))/;
   print join "\t", map { s/(?<!\\)"//gr =~ s/\\"/"/gr } split $re;
'

Awk

awk -v Q=\" -v FPAT="([^,]*)|(\"[^\"]+\")" -v OFS="\t" '{
   for (i=1; i<=NF; ++i)
      if ( substr($i, 1, 1) == Q )
         $i = substr($i, 2, length($i) - 2)
   print $1, $2, $3, $4, $5, $6, $7, $8
}'

Resultado:

A               C       D,E,F   G       I       K,L,M   Z

fonte
A versão +1 do Perl funciona como um encanto
ATorras 09/01
4

A solução termonuclear de mata-moscas deve estar usando o libreoffice. Enquanto https://ask.libreoffice.org/en/question/19042/is-is-possible-to-convert-comma-separated-value-csv-to-tab-separated-value-tsv-via-headless-mode / sugere que isso não é possível, mas está errado (ou está desatualizado?) e o seguinte comando funciona no meu 5.3:

loffice "-env:UserInstallation=file:///tmp/LibO_Conversion" --convert-to csv:"Text - txt - csv (StarCalc)":9,34,UTF8 --headless --outdir some/path --infilter='csv:44,34,UTF8' *.csv

o envargumento pode ser ignorado, mas dessa forma os documentos não aparecerão no seu documento recente.

chx
fonte
2
Eu acho que o verdadeiro mata-moscas termonucleares estaria escrevendo um utilitário Java para fazê-lo através da API UNO do LibreOffice :).
Pont
3

Se você possui ou pode instalar o csvtoolutilitário:

csvtool -t COMMA -u TAB cat in.csv > out.ctv

Observe que, por algum motivo csvtool, não possui uma página de manual, mas csvtool --helpimprimirá algumas centenas de linhas de documentação.

Keith Thompson
fonte
3

O uso mlré quase sucinto, mas a desativação de cabeçalhos requer opções longas:

mlr --c2t --implicit-csv-header --headerless-csv-output cat file.csv 

Saída:

A       C   D,E,F   G   I   K,L,M   Z
agc
fonte
3

Eu criei um conversor de código aberto CSV para TSV que lida com as transformações descritas. É bastante rápido, pode valer a pena dar uma olhada se houver uma necessidade contínua de converter arquivos CSV grandes. A ferramenta faz parte do kit de ferramentas de utilitários TSV do eBay (documentação csv2tsv aqui ). As opções padrão são suficientes para a entrada descrita:

$ csv2tsv file.csv > file.tsv
JonDeg
fonte
2

Vim

Apenas por diversão, as substituições regex podem ser executadas no Vim . Aqui está uma solução em potencial de quatro linhas, adaptada de: /programming/33332871/remove-all-commas-between-quotes-with-a-vim-regex

  1. As vírgulas entre aspas são primeiro alteradas para sublinhados (ou outro caractere ausente),
  2. Todas as outras vírgulas são substituídas por guias,
  3. Os sublinhados dentro das aspas são restaurados para vírgulas,
  4. As aspas são removidas.

    :%s/".\{-}"/\=substitute(submatch(0), ',', '_' , 'g')/g
    :%s/,/\t/g
    :%s/_/,/g
    :%s/"//g

Para criar um script da solução, as quatro linhas acima (sem dois pontos à esquerda) podem ser salvas em um arquivo, por exemplo to_tsv.vim. Abra cada CSV para editar com o Vim e sourceo to_tsv.vimscript na linha de comando do Vim (adaptado de /programming/3374179/run-vim-script-from-vim-commandline/8806874#8806874 ):

    :source /path/to/vim/filename/to_tsv.vim
jubilatious1
fonte
1

Aqui está o exemplo de conversão de CSV em TSV usando o jqutilitário :

$ jq -rn '@tsv "\(["A","","C","D,E,F","G","I","K,L,M","Z"])"'
A       C   D,E,F   G   I   K,L,M   Z

ou:

$ echo '["A","","C","D,E,F","G","I","K,L,M","Z"]' | jq -r @tsv
A       C   D,E,F   G   I   K,L,M   Z

No entanto, o formato CSV precisa ser bem formatado, portanto, cada sequência precisa ser citada.

Fonte: formato de saída TSV simples .

kenorb
fonte
1

Com perl, supondo que os campos csv não tenham "linhas ou guias incorporadas ou novas:

perl -pe 's{"(.*?)"|,}{$1//"\t"}ge'
Stéphane Chazelas
fonte
0

A seguir, é simplesmente uma correção para a resposta do @tripleee, de modo que retire todas as aspas do campo final, assim como ocorre com todos os outros campos.

Para mostrar o que está sendo corrigido, abaixo está a resposta de um triplo , além de uma ligeira modificação nos dados de exemplo do OP com aspas adicionais ao redor do campo ' Z ' final .

echo 'A,,C,"D,E,F","G",I,"K,L,M","Z"' |  sed -r -e 's/("([^"]*)")?,/\2\t/g'
A       C   D,E,F   G   I   K,L,M   "Z"

Você pode ver que ' Z ' é deixado com aspas. Isso é diferente de como os campos internos são tratados. Por exemplo, o ' G ' não possui aspas.

O comando a seguir usa uma segunda substituição para limpar a coluna final:

echo 'A,,C,"D,E,F","G",I,"K,L,M","Z"' |  sed -r -e 's/("([^"]*)")?,/\2\t/g' \
                                                -e 's/\t"([^"]*)"$/\t\1/'
A       C   D,E,F   G   I   K,L,M   Z
Fonnae
fonte
1
Quando os dados de entrada 'A,,C,"D,E,F","G",I,"K,L,M","Z,A"'são inseridos nesta resposta, eles "Z,A"são substituídos incorretamente por Z A, e não pelos corretos Z,A.
AGC