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.


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


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


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)

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.


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


Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.