Konwersja CSV na TSV

27

Mam wiele dużych plików CSV i chciałbym je w formacie TSV (format rozdzielany tabulatorami). Problem polega na tym, że w polach pliku CSV znajdują się przecinki, np .:

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

Oczekiwany wynik:

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

(gdzie białe spacje to „twarde” karty)

Mam Perla, Pythona i coreutils zainstalowane na tym serwerze.

Ciemne serce
źródło
Zrobiłbym to za pomocą node.js lub perla.
Peter mówi, że przywróć Monikę
1
Zamień cudzysłowy na tabulatory ...
cricket_007 19.04.17
Tak, gdybym miał więcej niż 5 minut na to pytanie. Ale z radością poprę moje odpowiedzi głosami. To, co próbowałem powiedzieć, że wspólne rzeczy sed / awk prawdopodobnie nie kwalifikują się do tego (przynajmniej w ich powszechnie używanym użyciu).
Peter mówi, że przywróć Monikę
6
Nie jestem pewien, czy twój przykład reprezentuje rzeczywiste dane, ale jeśli będą to rzeczywiste ciągi tekstowe, nie zapominaj, że może zajść potrzeba obsłużenia przypadku, w którym ciąg zawiera tabulator ...
AC
3
Inną trudną częścią jest to, że CSV jest bardzo luźno zdefiniowanym formatem, nie ma prawdziwego standardu (istnieje RFC, ale został napisany wiele lat po fakcie). Napisałem kod, który używał dostarczonego przez język analizatora składni CSV, a następnie musiałem go przepisać za pomocą niestandardowego analizatora składni, ponieważ znalazłem dane wejściowe w zepsutym wariancie formatu csv.
płukanie

Odpowiedzi:

37

Pyton

Dodaj do pliku o nazwie csv2tab.shi uczyń go wykonywalnym

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

Przebiegi testowe

$ 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
krykiet_007
źródło
5
Możliwy błąd: ta odpowiedź nie wymyka się wewnętrznym kartom.
Morgen,
4
@Morgen csv.writer(sys.stdout, dialect='excel-tab').writerows(csv.reader(sys.stdin))? Eliminuje również pętlę.
muru
1
@chx spróbuj python -c 'import csv,sys; csv.writer(sys.stdout, dialect="excel-tab").writerows(csv.reader(sys.stdin))'. Wątpię -m, żeby tak działało.
muru
18

Dla zabawy sed.

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

Jeśli sednie obsługuje -E, spróbuj -r. Jeśli sednie obsługujesz \tliteralnej tabulacji, spróbuj umieścić literalną tabulację (w wielu powłokach, ctrl- v tab) lub w Bash, użyj ciągu w $'...'stylu C (w takim przypadku odwrotny ukośnik w \2musi zostać podwojony). Jeśli chcesz zachować cudzysłowy, użyj \1zamiast \2(w takim przypadku wewnętrzna para nawiasów jest bezużyteczna i może zostać usunięta).

Nie czyni to próby obsługi podwójnych cudzysłowów w cudzysłowie; niektóre dialekty CSV obsługują to, podwajając cytowany podwójny cytat (sic).

potrójny
źródło
1
Myślę, że próbowałem około 100 różnych skryptów sed, aby to osiągnąć, ale wszystkie moje próby zakończyły się niepowodzeniem. To jest niesamowite.
George Vasiliou,
16

Za pomocą csvkitnarzędzia (Python), na przykład:

$ csvformat -T in.csv > out.txt

Czy streaming, z poprawnym cytowaniem CSV i TSV i ucieczką

Jest w apt i innych menedżerach pakietów

Neil McGuigan
źródło
13

Jedną z opcji może być moduł Text :: CSV perla np

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

demonstrować

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
steeldriver
źródło
1
Nie byłoby poprawne, gdyby pole zawierało zakładkę
Neil McGuigan 19.04.17
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
}'

Wynik:

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

źródło
Wersja +1 Perl działa jak urok
ATorras
4

W termojądrowym roztworze flyswatter musi być używany libreoffice. Podczas gdy https://ask.libreoffice.org/en/question/19042/is-is-possible-to-convert-comma-separated-value-csv-to-tab-separated-value-tsv-via-headless-mode / sugeruje, że nie jest to możliwe, ale jest złe (lub po prostu nieaktualne?), a następujące polecenie działa na moim 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

envargumentem może być pominięte, ale w ten sposób dokumenty nie pojawi się w ostatnim dokumencie.

chx
źródło
2
Myślę, że prawdziwą termojądrową falą lotną byłoby pisanie narzędzia Java, aby to zrobić za pośrednictwem UNO API LibreOffice :).
Pont
3

Jeśli masz lub możesz zainstalować csvtoolnarzędzie:

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

Zauważ, że z jakiegoś powodu csvtoolnie ma strony podręcznika, ale csvtool --helpwydrukuje kilkaset wierszy dokumentacji.

Keith Thompson
źródło
3

Korzystanie mlrjest prawie zwięzłe, ale wyłączenie nagłówków wymaga długich opcji:

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

Wydajność:

A       C   D,E,F   G   I   K,L,M   Z
agc
źródło
3

Jestem autorem konwertera CSV na TSV typu open source, który obsługuje opisane transformacje. Jest dość szybki, może warto go sprawdzić, jeśli istnieje potrzeba konwertowania dużych plików CSV. Narzędzie jest częścią eBay's TSV Utilities Toolkit (dokumentacja csv2tsv tutaj ). Wystarczą domyślne opcje dla opisanych danych wejściowych:

$ csv2tsv file.csv > file.tsv
JonDeg
źródło
2

Wigor

Dla zabawy w Vimie można wykonywać zamiany wyrażeń regularnych . Oto potencjalne czteroliniowe rozwiązanie, zaadaptowane z: /programming/33332871/remove-all-commas-between-quotes-with-a-vim-regex

  1. Przecinki między cudzysłowami są najpierw zamieniane na podkreślenia (lub inne nieobecne znaki),
  2. Wszystkie inne przecinki są zastępowane tabulatorami,
  3. Podkreślenia w cudzysłowie są przywracane do przecinków,
  4. Znaki cudzysłowu są usuwane.

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

Aby nieco skrypty rozwiązania, cztery wiersze powyżej (bez dwukropka) można zapisać do pliku, np to_tsv.vim. Otwórz każdy plik CSV do edycji z Vima i sourcedo to_tsv.vimskryptu na Vima linii poleceń (dostosowany od /programming/3374179/run-vim-script-from-vim-commandline/8806874#8806874 ):

    :source /path/to/vim/filename/to_tsv.vim
jubilat1
źródło
1

Oto przykład konwersji CSV na TSV za pomocą jqnarzędzia :

$ 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

lub:

$ 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

Jednak format CSV musi być dobrze sformatowany, więc każdy ciąg musi być cytowany.

Źródło: prosty format wyjściowy TSV .

kenorb
źródło
1

Przy perlzałożeniu, że pola csv nie mają osadzonych "ani nowych linii lub kart:

perl -pe 's{"(.*?)"|,}{$1//"\t"}ge'
Stéphane Chazelas
źródło
0

Poniżej znajduje się po prostu korekta odpowiedzi @tripleee, dzięki czemu usuwa cudzysłowy z ostatniego pola, tak jak w przypadku wszystkich innych pól.

Aby pokazać, co jest poprawiane, poniżej znajduje się odpowiedź trójki oraz niewielka modyfikacja przykładowych danych OP z dodanymi cudzysłowami wokół końcowego pola „ 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"

Widać, że „ Z ” zostało otoczone cudzysłowami. Różni się to od obsługi pól wewnętrznych. Na przykład „ G ” nie zawiera cudzysłowów.

Następujące polecenie używa drugiego podstawienia do wyczyszczenia ostatniej kolumny:

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
źródło
1
Gdy dane wejściowe 'A,,C,"D,E,F","G",I,"K,L,M","Z,A"'są wprowadzane do tej odpowiedzi, wówczas "Z,A"jest ona niepoprawnie zastępowana przez Z A, a nie poprawna Z,A.
agc