Перетворення CSV в TSV


27

У мене є декілька великих CSV-файлів і хотілося б їх у форматі TSV (формат розділений на вкладці). Ускладнення полягає в тому, що в полях файлу CSV є коми, наприклад:

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

Очікуваний вихід:

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

(де пробіли між ними - "жорсткі" вкладки)

У мене на цьому сервері встановлені Perl, Python та coreutils.


Я б це зробив за допомогою node.js або perl.
Peterh каже, що повернути Моніку

1
Замініть
коти, що

Так, якби у мене було більше 5 хвилин на це питання. Але я із задоволенням підтримаю відповідей своїми голосами. Що я намагався сказати, що звичайні речі sed / awk для цього, мабуть, не підлягають (принаймні, у звичайному вживанні).
Peterh каже, що повернути Моніку

6
Я не впевнений, чи ваш приклад відображає фактичні дані, але якщо вони будуть фактичними текстовими рядками, то не забудьте, що вам може знадобитися обробляти випадок, коли рядок містить вкладку ...
AC

3
Інша складна частина полягає в тому, що CSV - це дуже слабко визначений формат, немає реального стандарту (є RFC, але це було написано через роки після факту). Я написав код, який використовував мовний аналізатор CSV, а потім довелося переписати його за допомогою спеціального аналізатора, оскільки я виявив, що вхідні дані знаходяться у зламаному варіанті формату CSV.
підключення

Відповіді:


37

Пітон

Додайте до імені файл csv2tab.shі зробіть його виконуваним

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

Тестові запускання

$ 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
Можлива помилка: ця відповідь не уникає внутрішніх вкладок.
Морген

4
@Морген csv.writer(sys.stdout, dialect='excel-tab').writerows(csv.reader(sys.stdin))? Також усуває петлю.
муру

1
@chx спробуйте python -c 'import csv,sys; csv.writer(sys.stdout, dialect="excel-tab").writerows(csv.reader(sys.stdin))'. Я сумніваюся, -mщо так працює.
муру

18

Для розваги , sed.

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

Якщо ваш sedне підтримує -E, спробуйте -r. Якщо ваша sedне підтримує \tбуквальну вкладку, спробуйте вкласти буквальну вкладку (у багатьох оболонках, ctrl- v tab) або в Bash, використовуйте $'...'рядок у стилі C (у такому випадку зворотну косу рису \2потрібно подвоїти). Якщо ви хочете зберегти лапки, використовуйте \1замість \2(у такому випадку внутрішня пара дужок марна, і їх можна видалити).

Це не робить спроб обробити уникнулі подвійні лапки всередині подвійних лапок; деякі діалекти CSV підтримують це шляхом подвоєння наведеної подвійної цитати (sic).


1
Я думаю, що я спробував близько 100 різних сценаріїв sed, щоб досягти цього, але всі мої спроби зазнали невдачі. Це круто.
Джордж Васильоу

16

Використовуючи csvkitутиліту (Python), наприклад:

$ csvformat -T in.csv > out.txt

Здійснює потокове передавання з правильним цитуванням CSV та TSV та вихід

Це у доречних та інших менеджерів пакетів


13

Одним із варіантів може бути модуль Text :: CSV perl, наприклад

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

демонструвати

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
Неправильно, якщо поле містить вкладку
Ніл МакГуйган,

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
}'

Результат:

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

+1 Версія Perl працює як чарівність
ATorras

4

Розчин термоядерної мухоловки повинен використовувати лібріофіс. Поки https://ask.libreoffice.org/en/question/19042/is-is-possible-to-convert-comma-separated-value-csv-to-tab-separated-value-tsv-via-headless-mode / припускає, що це неможливо, але це неправильно (або просто застаріло?), і наступна команда працює на моєму 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

envаргумент може бути пропущений , але таким чином документи не будуть відображатися в останньому документі.


2
Я думаю, що справжній термоядерний маховик писав би утиліту Java, щоб це зробити за допомогою UNO API LibreOffice :).
Понт

3

Якщо у вас є або можете встановити цю csvtoolутиліту:

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

Зауважте, що з певних причин csvtoolнемає довідкової сторінки, але csvtool --helpбуде надруковано пару сотень рядків документації.


3

Використання mlrмайже стисло, але вимкнення заголовків вимагає довгих варіантів:

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

Вихід:

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

3

Я був автором перетворювача CSV в TSV з відкритим кодом, який обробляє описані перетворення. Це досить швидко, можливо, варто поглянути, якщо є необхідність конвертувати великі файли CSV. Інструмент є частиною інструментарію інструментів утиліти eBay (документація csv2tsv тут ). Варіантів за замовчуванням достатньо для описаного входу:

$ csv2tsv file.csv > file.tsv

2

Вим

Тільки для задоволення, заміни регулярного виразу можна проводити у Vim . Ось потенційне чотири рядкове рішення, адаптоване з: /programming/33332871/remove-all-commas-between-quotes-with-a-vim-regex

  1. Коми між цитатами спочатку змінюються на підкреслення (або інший відсутній символ),
  2. Усі інші коми замінюються вкладками,
  3. Підкреслення всередині лапок повертаються до коми,
  4. Лапки видалено.

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

Щоб дещо скриптувати рішення, чотири рядки вище (без ведучої двокрапки) можна зберегти у файл, наприклад to_tsv.vim. Відкрийте кожен CSV для редагування з Vim і sourceв to_tsv.vimскрипті на Vim командного рядка (адаптовано з /programming/3374179/run-vim-script-from-vim-commandline/8806874#8806874 ):

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

1

Ось приклад перетворення CSV в TSV за допомогою jqутиліти :

$ 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

або:

$ 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

Однак формат CSV повинен бути добре відформатований, тому кожну рядок потрібно цитувати.

Джерело: Простий формат виходу TSV .


1

З perl, якщо припустити, що в полях csv немає вбудованих "чи нових рядків чи вкладок:

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

0

Далі - це просто виправлення відповіді від @tripleee щоб вона будь-які лапки з остаточного поля так само, як і для всіх інших полів.

Щоб показати, що виправлено, нижче - відповідь трійки , а також незначна зміна прикладних даних ОП із доданими цитатами навколо остаточного поля " 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"

Ви можете бачити, що " Z " залишено цитатами навколо нього. Це відрізняється від того, як обробляються внутрішні поля. Наприклад, у " G " немає цитат.

Наступна команда використовує другу заміну для очищення остаточного стовпця:

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
Коли вхідні дані 'A,,C,"D,E,F","G",I,"K,L,M","Z,A"'вводяться в цю відповідь, то "Z,A"помилково замінюються Z A, а не правильними Z,A.
agc
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.