เอาต์พุตอาร์เรย์เป็น CSV ใน Ruby


185

มันง่ายพอที่จะอ่านไฟล์ CSV ลงในอาร์เรย์ที่มี Ruby แต่ฉันไม่พบเอกสารที่ดีเกี่ยวกับวิธีเขียนอาร์เรย์ลงในไฟล์ CSV ใครช่วยบอกฉันถึงวิธีการทำเช่นนี้?

ฉันใช้ Ruby 1.9.2 ถ้าเป็นเช่นนั้น


3
คำตอบที่คุณมีนั้นยอดเยี่ยม แต่ให้ฉันแนะนำให้คุณไม่ใช้ CSV หากคุณไม่มีแท็บในข้อมูลของคุณไฟล์ที่คั่นด้วยแท็บจะจัดการได้ง่ายกว่าเพราะแท็บเหล่านั้นไม่มีส่วนเกี่ยวข้องกับการอ้างถึงและการหลบหนีของ freakin มากมาย หากคุณต้องใช้ CSV แน่นอนว่ามันเป็นตัวแบ่ง
Bill Dueber

8
@Bill โมดูล CSV จัดการไฟล์ที่คั่นด้วยแท็บอย่างเรียบร้อยรวมถึงไฟล์ csv จริง ตัวเลือก: col_sep ช่วยให้คุณระบุตัวคั่นคอลัมน์เป็น "\ t" และทั้งหมดได้
Tamamis

1
นี่คือข้อมูลเพิ่มเติมเกี่ยวกับ CSV docs.ruby-lang.org/en/2.1.0/CSV.html
veeresh yh

การใช้ไฟล์. tab กับโมดูลนี้เป็นสิ่งที่ฉันกำลังทำอยู่เพราะการเปิดไฟล์นี้ใน Excel โดยไม่ตั้งใจจะทำให้การเข้ารหัสเกิดความสับสน…
MrVocabulary

คำตอบ:


326

ไปยังไฟล์:

require 'csv'
CSV.open("myfile.csv", "w") do |csv|
  csv << ["row", "of", "CSV", "data"]
  csv << ["another", "row"]
  # ...
end

ถึงสตริง:

require 'csv'
csv_string = CSV.generate do |csv|
  csv << ["row", "of", "CSV", "data"]
  csv << ["another", "row"]
  # ...
end

นี่คือเอกสารปัจจุบันเกี่ยวกับ CSV: http://ruby-doc.org/stdlib/libdoc/csv/rdoc/index.html


1
@ David เป็นโหมดไฟล์ "w" หมายถึงเขียนไปยังไฟล์ หากคุณไม่ได้ระบุสิ่งนี้มันจะเริ่มต้นที่ "rb" (โหมดไบนารีแบบอ่านอย่างเดียว) และคุณจะได้รับข้อผิดพลาดเมื่อพยายามเพิ่มลงในไฟล์ csv ของคุณ ดูruby-doc.org/core-1.9.3/IO.htmlสำหรับรายการโหมดไฟล์ที่ถูกต้องใน Ruby
Dylan Markow

15
Gotcha และสำหรับผู้ใช้ในอนาคตหากคุณต้องการให้การวนซ้ำแต่ละครั้งไม่เขียนทับไฟล์ csv ก่อนหน้านี้ให้ใช้ตัวเลือก "ab"
boulder_ruby

1
ดูคำตอบสำหรับไฟล์ทับทิมโหมด IO: stackoverflow.com/a/3682374/224707
Nick

38

ฉันได้รับสิ่งนี้ลงไปเพียงหนึ่งบรรทัด

rows = [['a1', 'a2', 'a3'],['b1', 'b2', 'b3', 'b4'], ['c1', 'c2', 'c3'], ... ]
csv_str = rows.inject([]) { |csv, row|  csv << CSV.generate_line(row) }.join("")
#=> "a1,a2,a3\nb1,b2,b3\nc1,c2,c3\n" 

ทำทั้งหมดข้างต้นและบันทึกลงใน csv ในหนึ่งบรรทัด

File.open("ss.csv", "w") {|f| f.write(rows.inject([]) { |csv, row|  csv << CSV.generate_line(row) }.join(""))}

บันทึก:

หากต้องการแปลงฐานข้อมูลบันทึกที่ใช้งานเป็น csv ฉันคิดว่าอย่างนี้

CSV.open(fn, 'w') do |csv|
  csv << Model.column_names
  Model.where(query).each do |m|
    csv << m.attributes.values
  end
end

Hmm @tamouse ส่วนสำคัญที่ทำให้ฉันสับสนโดยไม่ได้อ่านซอร์ส csv แต่โดยทั่วไปแล้วสมมติว่าแฮชแต่ละตัวในอาร์เรย์ของคุณมีจำนวนคู่ k / v เท่ากันและที่คีย์ต่างกันเสมอในลำดับเดียวกัน (เช่น หากข้อมูลของคุณมีโครงสร้าง) สิ่งนี้ควรกระทำ:

rowid = 0
CSV.open(fn, 'w') do |csv|
  hsh_ary.each do |hsh|
    rowid += 1
    if rowid == 1
      csv << hsh.keys# adding header row (column labels)
    else
      csv << hsh.values
    end# of if/else inside hsh
  end# of hsh's (rows)
end# of csv open

หากข้อมูลของคุณไม่มีโครงสร้างสิ่งนี้จะไม่สามารถใช้งานได้


ฉันดึงไฟล์ CSV โดยใช้ CSV.table จัดการบางอย่างกำจัดคอลัมน์บางส่วนและตอนนี้ฉันต้องการสปูล Array of Hashes ผลลัพธ์อีกครั้งในรูปแบบ CSV (คั่นด้วยแท็บจริงๆ) ทำอย่างไร? gist.github.com/4647196
tamouse

อืมม ... สรุปสาระสำคัญที่ค่อนข้างทึบแสง แต่ให้อาร์เรย์ของ hashes ทั้งหมดที่มีหมายเลขเดียวกันของ k / V คู่และกุญแจเดียวกันในลำดับเดียวกัน ...
boulder_ruby

ขอบคุณ @boulder_ruby ที่จะได้ผล ข้อมูลเป็นตารางการสำรวจสำมะโนประชากรและส่วนสำคัญที่ค่อนข้างทึบแสงมองกลับไปที่มัน :) โดยทั่วไปการแยกคอลัมน์บางอย่างจากตารางการสำรวจสำมะโนประชากรดั้งเดิมเป็นชุดย่อย
Tamamis

3
คุณใช้ผิดinjectที่นี่คุณต้องการใช้mapจริงๆ นอกจากนี้คุณไม่จำเป็นต้องส่งสตริงว่างให้joinเพราะนี่คือค่าเริ่มต้น ดังนั้นคุณสามารถลดขนาดให้ยิ่งกว่านี้:rows.map(&CSV.method(:generate_line).join
iGEL

1
ตัวอย่างที่สองของคุณซับซ้อนเกินไปเนื่องจากห้องสมุด CSV มีประสิทธิภาพมาก CSV.generate(headers: hsh.first&.keys) { |csv| hsh.each { |e| csv << e } }สร้าง CSV ที่เทียบเท่ากัน
Amadan

28

หากคุณมีอาร์เรย์ของข้อมูล:

rows = [["a1", "a2", "a3"],["b1", "b2", "b3", "b4"], ["c1", "c2", "c3"]]

จากนั้นคุณสามารถเขียนสิ่งนี้ลงไฟล์ด้วยสิ่งต่อไปนี้ซึ่งฉันคิดว่าง่ายกว่ามาก:

require "csv"
File.write("ss.csv", rows.map(&:to_csv).join)

20

หากใครสนใจนี่คือ liners หนึ่ง (และบันทึกการสูญเสียข้อมูลประเภทใน CSV):

require 'csv'

rows = [[1,2,3],[4,5]]                    # [[1, 2, 3], [4, 5]]

# To CSV string
csv = rows.map(&:to_csv).join             # "1,2,3\n4,5\n"

# ... and back, as String[][]
rows2 = csv.split("\n").map(&:parse_csv)  # [["1", "2", "3"], ["4", "5"]]

# File I/O:
filename = '/tmp/vsc.csv'

# Save to file -- answer to your question
IO.write(filename, rows.map(&:to_csv).join)

# Read from file
# rows3 = IO.read(filename).split("\n").map(&:parse_csv)
rows3 = CSV.read(filename)

rows3 == rows2   # true
rows3 == rows    # false

หมายเหตุ: CSV สูญเสียข้อมูลทุกประเภทคุณสามารถใช้ JSON เพื่อเก็บรักษาข้อมูลประเภทพื้นฐานหรือไปที่ verbose (แต่แก้ไขได้ง่ายกว่ามนุษย์) YAML เพื่อรักษาข้อมูลประเภททั้งหมด - ตัวอย่างเช่นหากคุณต้องการประเภทวันที่ซึ่งจะกลายเป็น สตริงใน CSV & JSON


9

การสร้างคำตอบของ @ boulder_ruby นี่คือสิ่งที่ฉันกำลังมองหาโดยสมมติว่าus_ecoมีตาราง CSV มาจากส่วนสำคัญของฉัน

CSV.open('outfile.txt','wb', col_sep: "\t") do |csvfile|
  csvfile << us_eco.first.keys
  us_eco.each do |row|
    csvfile << row.values
  end
end

อัปเดตส่วนสำคัญที่https://gist.github.com/tamouse/4647196


2

ดิ้นรนกับตัวเอง นี่คือของฉัน:

https://gist.github.com/2639448 :

require 'csv'

class CSV
  def CSV.unparse array
    CSV.generate do |csv|
      array.each { |i| csv << i }
    end
  end
end

CSV.unparse [ %w(your array), %w(goes here) ]

Btw ระวังอาเรย์หลายมิติในแงะ JRuby [ %w(your array), %w(goes here) ]จะไม่ดูสวย github.com/pry/pry/issues/568
Felix Rabe
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.