2011-01-27 13 views
134

È facile leggere un file CSV in un array con Ruby ma non riesco a trovare alcuna documentazione valida su come scrivere un array in un file CSV. Qualcuno può dirmi come farlo?Matrice di output in formato CSV in Ruby

Sto usando Ruby 1.9.2 se questo è importante.

+3

La risposta si ha è grandioso, ma lascia che ti chieda di non usare CSV. Se non si dispone di schede nei dati, i file delimitati da tabulazioni sono molto più facili da gestire in quanto non comportano così tanto citazioni, fughe e simili. Se devi usare CSV, ovviamente, sono le interruzioni. –

+7

@Bill, il modulo CSV gestisce in modo ordinato i file delimitati da tabulazioni e i file csv effettivi. L'opzione: col_sep consente di specificare il separatore di colonne come "\ t" e tutto va bene. – tamouse

+1

qui c'è più informazioni su CSV http://docs.ruby-lang.org/en/2.1.0/CSV.html –

risposta

246

in un file:

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

Per una stringa:

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

Ecco la documentazione corrente CSV: http://ruby-doc.org/stdlib/libdoc/csv/rdoc/index.html

+0

A cosa serve la "w"? –

+1

@David è la modalità file. "w" significa scrivere su un file. Se non lo specifichi, verrà impostato su "rb" (modalità binaria di sola lettura) e verrà visualizzato un errore quando si tenta di aggiungere al file csv. Vedi http://www.ruby-doc.org/core-1.9.3/IO.html per un elenco di modalità file valide in Ruby. –

+12

Gotcha. E per gli utenti futuri, se si desidera che ciascuna iterazione non sovrascriva il file csv precedente, utilizzare l'opzione "ab". –

2

Lottando con questo me stesso. Questo è il mio introito:

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) ] 
+0

Btw, fare attenzione agli array multidimensionali in corso su JRuby. '[% w (il tuo array),% w (va qui)]' non sembra carino. https://github.com/pry/pry/issues/568 –

27

Ho questo verso il basso per una sola riga.

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" 

fare tutto quanto sopra e Salva come csv, in una sola riga.

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

NOTA:

per convertire un database di record attivo in formato CSV sarebbe qualcosa come questo penso

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

Hmm @tamouse, che succo è motivo di confusione per me senza leggendo il sorgente csv, ma genericamente, supponendo che ogni hash nell'array abbia lo stesso numero di coppie k/v & che le chiavi siano sempre le stesse, nello stesso ordine (cioè se i tuoi dati sono strutturati), questo dovrebbe fare il rogito:

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 

Se i dati non è strutturato questo ovviamente non funzionerà

+0

Ho inserito un file CSV utilizzando CSV.table, ho apportato alcune manipolazioni, eliminato alcune colonne e ora voglio eseguire di nuovo lo spooling della matrice risultante di Hash come CSV (veramente delimitato da tabulazioni). Come? https://gist.github.com/4647196 – tamouse

+0

hmm ... l'essenza è alquanto opaca, ma data una serie di hash, tutti con lo stesso numero di coppie k/v e le stesse chiavi, nello stesso ordine .. –

+0

Grazie, @boulder_ruby. Funzionerà. I dati sono una tabella di censimento, e quell'aspetto è piuttosto opaco a guardarlo. :) In pratica sta estraendo alcune colonne dalla tabella del censimento originale in un sottoinsieme. – tamouse

7

Sulla @ boulder_ruby di risposta, questo è quello che sto cercando , supponendo che us_eco contenga la tabella CSV come dal mio gist.

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 

Aggiornato il succo al https://gist.github.com/tamouse/4647196

13

Se qualcuno è interessato, qui ci sono alcune battute (e una nota sulla perdita di informazioni di tipo 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 

Nota: CSV perde tutte le informazioni sul tipo, è possibile utilizzare JSON per conservare le informazioni di base del tipo o andare a YAML dettagliato (ma più facilmente modificabile dall'utente) per conservare tutte le informazioni sul tipo, ad esempio, se è necessario il tipo di data, che diventerebbe stringhe in CSV & JSON.

10

Se si dispone di un array di array di dati:

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

Quindi è possibile scrivere questo in un file con il seguente, che credo sia molto più semplice:

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