Umwandlung von CSV in TSV

27

Ich habe eine Reihe großer CSV-Dateien und möchte sie in TSV (tabulatorgetrenntes Format). Die Komplikation besteht darin, dass die Felder der CSV-Datei Kommas enthalten, z.

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

Erwartete Ausgabe:

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

(wobei Leerzeichen dazwischen "harte" Tabulatoren sind)

Ich habe Perl, Python und Coreutils auf diesem Server installiert.

Dunkles Herz
quelle
Ich würde dies mit node.js oder mit Perl tun.
Peterh sagt wieder Monica
1
Ersetzen Sie nicht zitierte Kommas durch Tabulatoren ...
cricket_007
Ja, wenn ich mehr als 5 Minuten Zeit hätte. Aber ich werde die Antwortenden gerne mit meinen Stimmen unterstützen. Was ich versucht habe zu sagen, dass die üblichen sed / awk-Dinge wahrscheinlich nicht dafür geeignet sind (zumindest in ihrer üblichen Verwendung).
Peterh sagt wieder Monica
6
Ich bin nicht sicher, ob Ihr Beispiel für die tatsächlichen Daten repräsentativ ist, aber wenn es sich um tatsächliche Textzeichenfolgen handelt, vergessen Sie nicht, dass Sie möglicherweise den Fall behandeln müssen, in dem die Zeichenfolge einen Tabulator enthält ...
AC
3
Der andere schwierige Teil ist, dass CSV ein sehr lose definiertes Format ist, es gibt keinen wirklichen Standard (es gibt einen RFC, der aber Jahre später geschrieben wurde). Ich habe Code geschrieben, der einen von der Sprache bereitgestellten CSV-Parser verwendete, und musste ihn dann mit einem benutzerdefinierten Parser neu schreiben, da ich feststellte, dass die Eingabedaten in einer fehlerhaften Variante des CSV-Formats vorliegen.
Plugwash

Antworten:

37

Python

Fügen Sie der Datei mit dem Namen hinzu csv2tab.shund machen Sie sie ausführbar

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

Testläufe

$ 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
quelle
5
Ein möglicher Fehler: Diese Antwort entgeht internen Tabs nicht.
Morgen
4
@ Morgen csv.writer(sys.stdout, dialect='excel-tab').writerows(csv.reader(sys.stdin))? Beseitigt auch die Schleife.
muru
1
@chx versuchen python -c 'import csv,sys; csv.writer(sys.stdout, dialect="excel-tab").writerows(csv.reader(sys.stdin))'. Ich bezweifle, -mdass das so funktioniert.
muru
18

Zum Spaß sed.

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

Wenn Ihr sednicht unterstützt -E, versuchen Sie es mit -r. Wenn Sie sedkeinen \tLiteral-Tabulator unterstützen, versuchen Sie, einen Literal-Tabulator (in vielen Shells, ctrl- v tab) oder eine $'...'C-Zeichenfolge (in diesem Fall muss der Backslash-In \2verdoppelt werden) in Bash zu setzen . Wenn Sie die Anführungszeichen beibehalten möchten, verwenden Sie \1stattdessen \2(in diesem Fall ist das innere Klammerpaar unbrauchbar und kann entfernt werden).

Dies unternimmt keinen Versuch, mit Anführungszeichen in Anführungszeichen umzugehen. Einige CSV-Dialekte unterstützen dies, indem sie das Anführungszeichen (sic) verdoppeln.

Tripleee
quelle
1
Ich denke, dass ich ungefähr 100 verschiedene sed-Skripte ausprobiert habe, aber alle meine Versuche sind gescheitert. Das ist fantastisch.
George Vasiliou
16

Mit dem csvkitDienstprogramm (Python), zum Beispiel:

$ csvformat -T in.csv > out.txt

Streaming mit korrektem CSV- und TSV-Anführungszeichen und Escapezeichen

Es ist in apt und anderen Paketmanagern

Neil McGuigan
quelle
13

Eine Option könnte Perls Text :: CSV- Modul sein, z

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

demonstrieren

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
Stahlfahrer
quelle
1
Wäre nicht korrekt, wenn ein Feld einen Tabulator enthält
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
}'

Ergebnis:

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

quelle
+1 Perl-Version funktioniert wie ein Zauber
ATorras
4

Für die Lösung mit thermonuklearer Fliegenklatsche muss libreoffice verwendet werden. Während https://ask.libreoffice.org/de/question/19042/ist-mglich-zu-konvertieren-der-getrennten-Wert-csv-zum-getrennten-Wert-tsv-über-kopflosen-Modus / schlägt vor, dass dies nicht möglich ist, aber es ist falsch (oder nur veraltet?) und der folgende Befehl funktioniert auf meinem 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

Das envArgument könnte übersprungen werden, aber auf diese Weise werden die Dokumente in Ihrem letzten Dokument nicht angezeigt.

chx
quelle
2
Ich denke, die wahre thermonukleare Fliegenklatsche würde ein Java-Dienstprogramm schreiben, um dies über die UNO-API von LibreOffice zu tun :).
Pont
3

Wenn Sie das csvtoolDienstprogramm haben oder installieren können :

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

Beachten Sie, dass aus irgendeinem Grund csvtoolkeine Manpage vorhanden ist, sondern csvtool --helpein paar hundert Zeilen Dokumentation gedruckt werden.

Keith Thompson
quelle
3

Die Verwendung mlrist fast prägnant, aber das Deaktivieren von Headern erfordert lange Optionen:

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

Ausgabe:

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

Ich habe einen Open-Source-Konverter von CSV zu TSV erstellt, der die beschriebenen Transformationen verarbeitet. Es ist recht schnell und kann einen Blick wert sein, wenn ständig große CSV-Dateien konvertiert werden müssen. Das Tool ist Teil des TSV-Toolkits von eBay (csv2tsv-Dokumentation hier ). Standardoptionen reichen für die beschriebene Eingabe aus:

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

Vim

Aus Spaß können Regex-Substitutionen in Vim durchgeführt werden . Hier ist eine mögliche vierzeilige Lösung, angepasst von: https://stackoverflow.com/questions/33332871/remove-all-commas-between-quotes-with-a-vim-regex

  1. Kommas zwischen Anführungszeichen werden zuerst in Unterstriche (oder andere fehlende Zeichen) geändert.
  2. Alle anderen Kommas werden durch Tabulatoren ersetzt.
  3. Unterstriche in Anführungszeichen werden in Kommas umgewandelt.
  4. Anführungszeichen werden entfernt.

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

Um die Lösung etwas zu skripten, können die vier obigen Zeilen (ohne vorangestelltem Doppelpunkt) in einer Datei gespeichert werden, z to_tsv.vim. Öffnen Sie jede CSV zum Bearbeiten mit Vim und sourcedem to_tsv.vimSkript in der Vim- Befehlszeile (angepasst von https://stackoverflow.com/questions/3374179/run-vim-script-from-vim-commandline/8806874#8806874 ):

    :source /path/to/vim/filename/to_tsv.vim
fröhlich1
quelle
1

Hier ist das Beispiel für die Konvertierung von CSV in TSV mit dem jqDienstprogramm :

$ 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

oder:

$ 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

Das CSV-Format muss jedoch gut formatiert sein, sodass jeder String in Anführungszeichen gesetzt werden muss.

Quelle: Einfaches TSV-Ausgabeformat .

Kenorb
quelle
1

Mit perl, vorausgesetzt, die CSV-Felder haben keine eingebetteten "Zeilenumbrüche oder Tabulatoren:

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

Das Folgende ist einfach eine Korrektur der Antwort von @tripleee, sodass alle Anführungszeichen aus dem letzten Feld entfernt werden, genau wie bei allen anderen Feldern.

Um zu zeigen, was korrigiert wird, finden Sie unten die Antwort eines Dreifachen sowie eine geringfügige Änderung der OP-Beispieldaten mit zusätzlichen Anführungszeichen um das letzte ' Z' -Feld.

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"

Sie können sehen, dass ' Z ' in Anführungszeichen gesetzt ist. Dies unterscheidet sich vom Umgang mit den inneren Feldern. Zum Beispiel enthält das ' G ' keine Anführungszeichen.

Der folgende Befehl verwendet eine zweite Ersetzung, um die letzte Spalte zu bereinigen:

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
quelle
1
Wenn die Eingabedaten 'A,,C,"D,E,F","G",I,"K,L,M","Z,A"'in diese Antwort eingegeben werden, wird die "Z,A"falsch durch Z Aund nicht durch die richtige ersetzt Z,A.
agc