Вихідний масив до CSV в Ruby


185

Досить просто читати файл CSV в масив з Ruby, але я не можу знайти жодної гарної документації щодо того, як записати масив у файл CSV. Хтось може сказати мені, як це зробити?

Я використовую Ruby 1.9.2, якщо це має значення.


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

8
@Bill, модуль CSV акуратно обробляє файли з обмеженими вкладками, а також фактичні файли CSV. Параметр: col_sep дозволяє вказати роздільник стовпців як "\ t", і все добре.
tamouse

1
Ось додаткова інформація про CSV docs.ruby-lang.org/uk/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.
Ділан Марков

15
Готча. А для майбутніх користувачів, якщо ви хочете, щоб кожна ітерація не перезаписувала попередній файл csv, скористайтеся опцією "ab".
boulder_ruby

1
Дивіться цю відповідь для режимів вводу
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, провів деякі маніпуляції, позбувся деяких стовпців, і тепер я хочу вивести отриманий масив хешей знову як CSV (дійсно з обмеженими вкладками). Як? gist.github.com/4647196
tamouse

хм ... ця суть дещо непрозора, але з урахуванням масиву хешів, все з однаковою кількістю к / п пар і однаковими клавішами, в тому ж порядку ...
boulder_ruby

Дякую, @boulder_ruby Це спрацює. Дані є переписною таблицею, і це суть непрозорого огляду на неї. :) Це в основному вилучення певних стовпців з початкової таблиці перепису в підмножину.
tamouse

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.
Амадан

28

Якщо у вас є масив масивів даних:

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

Потім ви можете написати це у файл із наступним, що, на мою думку, набагато простіше:

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

20

Якщо когось цікавить, ось кілька одноразових (і примітка про втрату інформації про тип у 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 для збереження основної інформації про тип або перейти до дослідної 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/isissue/568
Фелікс Рабе
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.