แปลง 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
แทนที่เครื่องหมายจุลภาคที่ไม่ใช่เครื่องหมายคำพูดแทนด้วยแท็บ ...
cricket_007

ใช่ถ้าฉันมีคำถามนี้นานกว่า 5 นาที แต่ฉันจะสนับสนุนผู้ตอบด้วยคะแนนของฉันอย่างมีความสุข สิ่งที่ฉันพยายามจะพูดว่าสิ่งที่ sed / awk ทั่วไปอาจไม่เหมาะสมสำหรับสิ่งนั้น (อย่างน้อยก็ในการใช้งานทั่วไปของพวกเขา)
peterh กล่าวว่าคืนสถานะโมนิก้า

6
ฉันไม่แน่ใจว่าตัวอย่างของคุณเป็นตัวแทนของข้อมูลจริงหรือไม่ แต่ถ้าสิ่งเหล่านั้นเป็นสตริงข้อความจริงอย่าลืมว่าคุณอาจต้องจัดการกับกรณีที่สตริงมีแท็บ ...
AC

3
ส่วนที่ยุ่งยากอื่น ๆ คือ CSV เป็นรูปแบบที่กำหนดไว้อย่างแน่นหนาไม่มีมาตรฐานจริง (มี RFC แต่เขียนมาหลายปีหลังจากข้อเท็จจริง) ฉันได้เขียนโค้ดที่ใช้ตัวแยกวิเคราะห์ CSV ที่ใช้ภาษาแล้วต้องเขียนใหม่ด้วยตัวแยกวิเคราะห์ที่กำหนดเองเพราะฉันพบว่าข้อมูลอินพุตนั้นอยู่ในรูปแบบแตกหักของรูปแบบ csv
plugwash

คำตอบ:


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
ข้อผิดพลาดที่เป็นไปได้: คำตอบนี้ไม่ได้หนีแท็บภายใน
Morgen

4
@Morgen csv.writer(sys.stdout, dialect='excel-tab').writerows(csv.reader(sys.stdin))? กำจัดการวนซ้ำเช่นกัน
muru

1
@chx python -c 'import csv,sys; csv.writer(sys.stdout, dialect="excel-tab").writerows(csv.reader(sys.stdin))'ลอง ฉันสงสัย-mว่าวิธีการทำงาน
muru

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 ตัวเพื่อให้ได้สิ่งนี้ นี่มันเจ๋งมาก.
George Vasiliou

16

การใช้csvkitยูทิลิตี้ (Python) ตัวอย่างเช่น:

$ csvformat -T in.csv > out.txt

ทำการสตรีมด้วยการอ้างอิงและหนีออกจาก CSV และ TSV ที่ถูกต้อง

มันอยู่ใน apt และผู้จัดการแพ็คเกจอื่น ๆ


13

ตัวเลือกหนึ่งอาจเป็นข้อความ ::โมดูล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
จะไม่ถูกต้องหากฟิลด์มีแท็บ
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
}'

ผล:

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

+1 รุ่น Perl ทำงานเหมือน
เครื่องราง

4

วิธีแก้ปัญหาความร้อนฟลูออนนิวเคลียร์ต้องใช้ libreoffice ในขณะที่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 :)
Pont

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 ขนาดใหญ่ เครื่องมือเป็นส่วนหนึ่งของชุดเครื่องมืออรรถประโยชน์ TSV ของ eBay (เอกสาร csv2tsv ที่นี่ ) ตัวเลือกเริ่มต้นเพียงพอสำหรับการป้อนข้อมูลที่อธิบาย:

$ csv2tsv file.csv > file.tsv

2

เป็นกลุ่ม

เพียงเพื่อความสนุกสนานแทน regex สามารถดำเนินการในกลุ่ม ต่อไปนี้เป็นโซลูชันสี่สายที่มีศักยภาพซึ่งดัดแปลงมาจาก: /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 สำหรับการแก้ไขด้วยเป็นกลุ่มและสคริปต์ในกลุ่มบรรทัดคำสั่ง (ดัดแปลงมาจาก/programming/3374179/run-vim-script-from-vim-commandline/8806874#8806874 ): sourceto_tsv.vim

    :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 เพื่อที่จะตัดเครื่องหมายคำพูดใด ๆ จากฟิลด์สุดท้ายเช่นเดียวกับฟิลด์อื่นทั้งหมด

เพื่อแสดงสิ่งที่ถูกแก้ไขด้านล่างนี้เป็นคำตอบของtripleeeรวมถึงการปรับเปลี่ยนเล็กน้อยในข้อมูลตัวอย่างของ OP ด้วยการเพิ่มเครื่องหมายคำพูดรอบฟิลด์ ' 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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.