Keluaran array ke CSV di Ruby

185

Cukup mudah untuk membaca file CSV ke dalam array dengan Ruby tetapi saya tidak dapat menemukan dokumentasi yang bagus tentang cara menulis array ke file CSV. Adakah yang bisa memberi tahu saya cara melakukan ini?

Saya menggunakan Ruby 1.9.2 jika itu penting.

Jason Swett
sumber
3
Jawaban yang Anda miliki luar biasa, tetapi izinkan saya mendorong Anda untuk tidak menggunakan CSV. Jika Anda tidak memiliki tab dalam data Anda, file dengan pembatas-tab jauh lebih mudah untuk ditangani karena mereka tidak melibatkan begitu banyak kutipan dan pelarian freakin dan semacamnya. Jika Anda harus menggunakan CSV, tentu saja, itu masalahnya.
Bill Dueber
8
@Bill, modul CSV dengan rapi menangani file-file yang dibatasi-tab dan juga file csv yang sebenarnya. Opsi: col_sep memungkinkan Anda menentukan pemisah kolom sebagai "\ t" dan semuanya baik-baik saja.
tamouse
1
di sini adalah Info lebih lanjut tentang CSV docs.ruby-lang.org/en/2.1.0/CSV.html
veeresh yh
Menggunakan file .tab dengan modul ini adalah apa yang saya lakukan, karena membuka ini di Excel secara tidak sengaja akan mengacaukan pengkodean ...
MrVocabulary

Jawaban:

326

Ke file:

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

Ke string:

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

Berikut dokumentasi terkini tentang CSV: http://ruby-doc.org/stdlib/libdoc/csv/rdoc/index.html

Dylan Markow
sumber
1
@ Bersihkan itu adalah mode file. "w" berarti menulis ke file. Jika Anda tidak menentukan ini, defaultnya adalah "rb" (mode biner read-only) dan Anda akan mendapatkan kesalahan ketika mencoba menambahkan ke file csv Anda. Lihat ruby-doc.org/core-1.9.3/IO.html untuk daftar mode file yang valid di Ruby.
Dylan Markow
15
Kena kau. Dan untuk pengguna di masa depan, jika Anda ingin setiap iterasi tidak menimpa file csv sebelumnya, gunakan opsi "ab".
boulder_ruby
1
Lihat jawaban ini untuk Mode IO File Ruby: stackoverflow.com/a/3682374/224707
Nick
38

Saya hanya punya satu baris.

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" 

Lakukan semua hal di atas dan simpan ke csv, dalam satu baris.

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

CATATAN:

Untuk mengonversi database rekaman aktif ke csv akan menjadi sesuatu seperti ini saya pikir

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

Hmm @tamouse, intinya agak membingungkan saya tanpa membaca sumber csv, tetapi secara umum, dengan asumsi setiap hash dalam array Anda memiliki jumlah pasangan k / v yang sama & bahwa kunci selalu sama, dalam urutan yang sama (yaitu jika data Anda terstruktur), ini harus melakukan akta:

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

Jika data Anda tidak terstruktur, ini jelas tidak akan berfungsi

boulder_ruby
sumber
Saya menarik file CSV menggunakan CSV.table, melakukan beberapa manipulasi, menghilangkan beberapa kolom, dan sekarang saya ingin menggulung Array of Hash yang dihasilkan keluar lagi sebagai CSV (benar-benar dibatasi oleh tab). Bagaimana caranya? gist.github.com/4647196
tamouse
hmm ... inti itu agak buram, tetapi diberi array hash, semua dengan jumlah yang sama dari pasangan k / v dan kunci yang sama, dalam urutan yang sama ...
boulder_ruby
Terima kasih, @boulder_ruby. Itu akan bekerja. Data adalah tabel sensus, dan intinya agak buram melihat kembali. :) Ini pada dasarnya mengekstraksi kolom tertentu dari tabel sensus asli menjadi subset.
tamouse
3
Anda menyalahgunakan di injectsini, Anda benar-benar ingin menggunakannya map. Selain itu, Anda tidak perlu meneruskan string kosong ke join, karena ini adalah default. Jadi, Anda bisa menyusutkan lebih jauh ke ini:rows.map(&CSV.method(:generate_line).join
iGEL
1
Contoh kedua Anda terlalu rumit, karena perpustakaan CSV cukup kuat. CSV.generate(headers: hsh.first&.keys) { |csv| hsh.each { |e| csv << e } }menghasilkan CSV yang setara.
Amadan
28

Jika Anda memiliki larik array data:

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

Kemudian Anda dapat menulis ini ke file dengan yang berikut ini, yang menurut saya jauh lebih sederhana:

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

Jika ada yang tertarik, berikut adalah beberapa kalimat (dan catatan tentang hilangnya informasi jenis di 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

Catatan: CSV kehilangan semua jenis informasi, Anda dapat menggunakan JSON untuk menyimpan informasi tipe dasar, atau pergi ke verbose (tetapi lebih mudah diedit oleh manusia) YAML untuk menyimpan semua informasi jenis - misalnya, jika Anda memerlukan jenis tanggal, yang akan menjadi string dalam CSV & JSON.

Kanat Bolazar
sumber
9

Membangun jawaban @ boulder_ruby, inilah yang saya cari, dengan asumsi us_ecoberisi tabel CSV dari intisari saya.

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

Diperbarui intinya di https://gist.github.com/tamouse/4647196

tamouse
sumber
2

Berjuang dengan ini sendiri. Ini adalah pendapat saya:

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) ]
Felix Rabe
sumber
Btw, waspadalah terhadap array multidimensi yang mengorek JRuby. [ %w(your array), %w(goes here) ]tidak akan terlihat cantik. github.com/pry/pry/issues/568
Felix Rabe