Array in CSV in Ruby ausgeben

185

Es ist einfach genug, eine CSV-Datei mit Ruby in ein Array einzulesen, aber ich kann keine gute Dokumentation zum Schreiben eines Arrays in eine CSV-Datei finden. Kann mir jemand sagen, wie das geht?

Ich verwende Ruby 1.9.2, wenn das wichtig ist.

Jason Swett
quelle
3
Die Antwort, die Sie haben, ist großartig, aber ich möchte Sie dringend bitten, CSV nicht zu verwenden. Wenn Ihre Daten keine Registerkarten enthalten, sind tabulatorgetrennte Dateien viel einfacher zu handhaben, da sie nicht so viel verdammtes Zitieren und Entkommen und dergleichen beinhalten. Wenn Sie CSV verwenden müssen, sind dies natürlich die Pausen.
Bill Dueber
8
@Bill, das CSV-Modul verarbeitet ordentlich durch Tabulatoren getrennte Dateien sowie tatsächliche CSV-Dateien. Mit der Option: col_sep können Sie das Spaltentrennzeichen als "\ t" angeben, und alles ist in Ordnung.
Tamouse
1
Hier ist mehr Info über CSV docs.ruby-lang.org/en/2.1.0/CSV.html
veeresh yh
Die Verwendung von Tab-Dateien mit diesem Modul ist meine Aufgabe, da ein versehentliches Öffnen in Excel die Codierung anderweitig durcheinander bringen würde…
MrVocabulary

Antworten:

326

Zu einer Datei:

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

Zu einer Zeichenfolge:

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

Hier ist die aktuelle Dokumentation zu CSV: http://ruby-doc.org/stdlib/libdoc/csv/rdoc/index.html

Dylan Markow
quelle
1
@ David ist es der Dateimodus. "w" bedeutet in eine Datei schreiben. Wenn Sie dies nicht angeben, wird standardmäßig "rb" (schreibgeschützter Binärmodus) verwendet, und beim Versuch, Ihrer CSV-Datei etwas hinzuzufügen, wird eine Fehlermeldung angezeigt. Eine Liste der gültigen Dateimodi in Ruby finden Sie unter ruby-doc.org/core-1.9.3/IO.html .
Dylan Markow
15
Erwischt. Wenn Sie für zukünftige Benutzer möchten, dass bei jeder Iteration die vorherige CSV-Datei nicht überschrieben wird, verwenden Sie die Option "ab".
boulder_ruby
1
Siehe diese Antwort für Ruby File IO-Modi: stackoverflow.com/a/3682374/224707
Nick
37

Ich habe das auf nur eine Zeile reduziert.

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" 

Führen Sie alle oben genannten Schritte aus und speichern Sie sie in einer Zeile in einem CSV.

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

HINWEIS:

Eine aktive Datensatzdatenbank in CSV zu konvertieren wäre ungefähr so, denke ich

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

Hmm @tamouse, das Wesentliche ist für mich etwas verwirrend, ohne die CSV-Quelle zu lesen, aber im Allgemeinen, vorausgesetzt, jeder Hash in Ihrem Array hat die gleiche Anzahl von k / v-Paaren und die Schlüssel sind immer gleich, in der gleichen Reihenfolge (dh Wenn Ihre Daten strukturiert sind, sollte dies die Tat tun:

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

Wenn Ihre Daten nicht strukturiert sind, funktioniert dies offensichtlich nicht

boulder_ruby
quelle
Ich habe eine CSV-Datei mit CSV.table eingezogen, einige Manipulationen vorgenommen, einige Spalten entfernt, und jetzt möchte ich das resultierende Array von Hashes wieder als CSV (wirklich tabulatorgetrennt) herausspulen. Wie man? gist.github.com/4647196
Tamouse
hmm ... das Wesentliche ist etwas undurchsichtig, aber mit einer Reihe von Hashes, alle mit der gleichen Anzahl von k / v-Paaren und den gleichen Schlüsseln, in der gleichen Reihenfolge ...
boulder_ruby
Danke, @boulder_ruby. Das wird funktionieren. Die Daten sind eine Zensus-Tabelle, und dieser Kern ist im Rückblick ziemlich undurchsichtig. :) Grundsätzlich werden bestimmte Spalten aus der ursprünglichen Zensus-Tabelle in eine Teilmenge extrahiert.
Tamouse
3
Sie missbrauchen injecthier, Sie wollen wirklich verwenden map. Außerdem müssen Sie keine leere Zeichenfolge übergeben join, da dies die Standardeinstellung ist. Sie könnten es also noch weiter verkleinern:rows.map(&CSV.method(:generate_line).join
iGEL
1
Ihr zweites Beispiel ist zu kompliziert, da die CSV-Bibliothek sehr leistungsfähig ist. CSV.generate(headers: hsh.first&.keys) { |csv| hsh.each { |e| csv << e } }generiert eine äquivalente CSV.
Amadan
28

Wenn Sie ein Array von Datenfeldern haben:

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

Dann können Sie dies in eine Datei mit den folgenden Angaben schreiben, was meiner Meinung nach viel einfacher ist:

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

Wenn jemand interessiert ist, hier einige Einzeiler (und ein Hinweis zum Verlust von Typinformationen in 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

Hinweis: CSV verliert alle Typinformationen. Sie können JSON verwenden, um grundlegende Typinformationen beizubehalten, oder YAML verwenden, um alle Typinformationen beizubehalten, z. B. wenn Sie einen Datumstyp benötigen Zeichenfolgen in CSV & JSON.

Kanat Bolazar
quelle
9

Aufbauend auf der Antwort von @ boulder_ruby ist dies das, wonach ich suche, vorausgesetzt, es us_ecoenthält die CSV-Tabelle aus meiner Sicht.

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

Das Wesentliche wurde unter https://gist.github.com/tamouse/4647196 aktualisiert

Tamouse
quelle
2

Ich habe selbst damit zu kämpfen. Das ist meine Meinung:

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
quelle
Übrigens, hüte dich vor mehrdimensionalen Arrays auf JRuby. [ %w(your array), %w(goes here) ]wird nicht schön aussehen. github.com/pry/pry/issues/568
Felix Rabe