Conversione da CSV a TSV


27

Ho un numero di file CSV di grandi dimensioni e li vorrei in TSV (formato separato da tabulazione). La complicazione è che ci sono virgole nei campi del file CSV, ad esempio:

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

Uscita prevista:

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

(dove gli spazi bianchi in mezzo sono schede 'difficili')

Ho Perl, Python e coreutils installati su questo server.


Lo farei con node.js o con perl.
Peter dice di reintegrare Monica il

1
Sostituisci le virgole non quotate con le schede ...
cricket_007,

Sì, se avessi più di 5 minuti a questa domanda. Ma sosterrò felicemente i rispondenti con i miei voti. Quello che ho cercato di dire, che le cose comuni sed / awk sono probabilmente non ammissibili per questo (almeno nel loro uso comunemente usato).
Peter dice di reintegrare Monica il

6
Non sono sicuro che il tuo esempio sia rappresentativo dei dati effettivi, ma se quelli saranno stringhe di testo effettive, non dimenticare che potresti dover gestire il caso in cui la stringa include una scheda ...
AC

3
L'altra parte difficile è che CSV è un formato molto vagamente definito, non esiste uno standard reale (esiste un RFC ma è stato scritto anni dopo il fatto). Ho scritto un codice che utilizzava un parser CSV fornito dal linguaggio e quindi ho dovuto riscriverlo con un parser personalizzato perché ho scoperto che i dati di input erano in una variante rotta del formato CSV.
lavaggio:

Risposte:


37

Pitone

Aggiungi al file denominato csv2tab.she rendilo eseguibile

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

Esecuzioni di test

$ 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

5
Un possibile bug: questa risposta non sfugge alle schede interne.
Morgen,

4
@Morgen csv.writer(sys.stdout, dialect='excel-tab').writerows(csv.reader(sys.stdin))? Elimina anche il loop.
Muru,

1
@chx try python -c 'import csv,sys; csv.writer(sys.stdout, dialect="excel-tab").writerows(csv.reader(sys.stdin))'. Dubito -mche funzioni in questo modo.
muru,

18

Per divertimento, sed.

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

Se il tuo sednon supporta -E, prova con -r. Se il tuo sednon supporta \tuna scheda letterale, prova a inserire una scheda letterale (in molte shell, ctrl- v tab) o in Bash, usa una $'...'stringa in stile C (nel qual caso la barra rovesciata \2deve essere raddoppiata). Se si desidera mantenere le virgolette, utilizzare \1invece di \2(nel qual caso la coppia di parentesi interna è inutile e può essere rimossa).

Ciò non tenta di gestire virgolette doppie di escape all'interno di virgolette doppie; alcuni dialetti CSV lo supportano raddoppiando la doppia virgoletta citata (sic).


1
Penso di aver provato circa 100 diversi script sed per ottenere questo, ma tutti i miei tentativi fallirono. Questo e spettacolare.
George Vasiliou,

16

Utilizzando l' csvkitutilità (Python), ad esempio:

$ csvformat -T in.csv > out.txt

Esegue lo streaming, con quotazioni e escape CSV e TSV corretti

È in apt e altri gestori di pacchetti


13

Un'opzione potrebbe essere il modulo Text :: CSV di perl, ad es

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

dimostrare

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

1
Non sarebbe corretto se un campo contiene una scheda
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
}'

Risultato:

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

+1 La versione Perl funziona come un incantesimo
ATorras

4

La soluzione termonucleare flyswatter deve utilizzare libreoffice. Mentre https://ask.libreoffice.org/en/question/19042/is-is-possible-to-convert-comma-separated-value-csv-to-tab-separated-value-tsv-via-headless-mode / suggerisce che questo non è possibile ma è sbagliato (o semplicemente obsoleto?) e il seguente comando funziona sul mio 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

l' envargomento potrebbe essere ignorato, ma in questo modo i documenti non verranno visualizzati nel documento recente.


2
Penso che il vero flyswatter termonucleare sarebbe scrivere un'utilità Java per farlo tramite l'API UNO di LibreOffice :).
Pont,

3

Se si dispone o è possibile installare l' csvtoolutilità:

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

Nota che per qualche motivo csvtoolnon ha una pagina man, ma csvtool --helpstamperà un paio di centinaia di righe di documentazione.


3

L'uso mlrè quasi sintetico, ma disabilitare le intestazioni richiede lunghe opzioni:

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

Produzione:

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

3

Ho creato un convertitore open source CSV in TSV che gestisce le trasformazioni descritte. È abbastanza veloce, può valere la pena dare un'occhiata se c'è una continua necessità di convertire file CSV di grandi dimensioni. Tool fa parte del toolkit di utilità TSV di eBay (documentazione csv2tsv qui ). Le opzioni predefinite sono sufficienti per l'input descritto:

$ csv2tsv file.csv > file.tsv

2

Vim

Solo per divertimento, le sostituzioni regex possono essere eseguite in Vim . Ecco una potenziale soluzione a quattro righe, adattata da: /programming/33332871/remove-all-commas-between-quotes-with-a-vim-regex

  1. Le virgole tra virgolette vengono prima modificate in caratteri di sottolineatura (o altro carattere assente),
  2. Tutte le altre virgole sono sostituite da schede,
  3. Le sottolineature all'interno delle virgolette vengono ripristinate in virgole,
  4. Le virgolette vengono rimosse.

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

Per scrivere un po 'la soluzione, le quattro righe sopra (senza i due punti iniziali) possono essere salvate in un file, ad es to_tsv.vim. Apri ogni CSV per la modifica con Vim e sourcelo to_tsv.vimscript nella riga di comando di Vim (adattato da /programming/3374179/run-vim-script-from-vim-commandline/8806874#8806874 ):

    :source /path/to/vim/filename/to_tsv.vim

1

Ecco l'esempio di conversione di CSV in TSV utilizzando l' jqutilità :

$ 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

o:

$ 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

Tuttavia, il formato CSV deve essere ben formattato, quindi ogni stringa deve essere quotata.

Fonte: formato di output TSV semplice .


1

Con perl, supponendo che i campi CSV non abbiano incorporati "o newline o tab:

perl -pe 's{"(.*?)"|,}{$1//"\t"}ge'

0

Quella che segue è semplicemente una correzione alla risposta di @tripleee in modo che rimuova tutte le virgolette dal campo finale proprio come fa per tutti gli altri campi.

Per mostrare ciò che viene corretto, di seguito è una risposta del tripleee , più una leggera modifica ai dati di esempio del PO con citazioni aggiunte attorno al campo finale " Z ".

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"

Puoi vedere che la ' Z ' è lasciata tra virgolette. Ciò è diverso da come vengono gestiti i campi interni. Ad esempio, la " G " non contiene virgolette.

Il comando seguente utilizza una seconda sostituzione per pulire la colonna finale:

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

1
Quando i dati di input vengono 'A,,C,"D,E,F","G",I,"K,L,M","Z,A"'immessi in questa risposta, "Z,A"viene sostituito in modo errato Z A, anziché corretto Z,A.
agc,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.