2011-01-27 25 views
134

Es bastante fácil leer un archivo CSV en una matriz con Ruby, pero no puedo encontrar ninguna buena documentación sobre cómo escribir una matriz en un archivo CSV. ¿Puede alguien decirme cómo hacer esto?Matriz de salida a CSV en Ruby

Estoy usando Ruby 1.9.2 si eso es importante.

+3

La respuesta tiene es genial, pero déjame instarte a no usar CSV. Si no tiene pestañas en sus datos, es mucho más fácil lidiar con los archivos delimitados por tabuladores, ya que no implican tanta cita y escapatoria, etc. Si debe usar CSV, por supuesto, son los descansos. –

+7

@Bill, el módulo CSV maneja perfectamente los archivos delimitados por tabuladores, así como los archivos csv reales. La opción: col_sep le permite especificar el separador de columna como "\ t" y todo está bien. – tamouse

+1

aquí hay más información sobre CSV http://docs.ruby-lang.org/es/2.1.0/CSV.html –

Respuesta

246

en un archivo:

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

en una cadena:

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

Aquí está la documentación actual en CSV: http://ruby-doc.org/stdlib/libdoc/csv/rdoc/index.html

+0

¿Para qué sirve la "w"? –

+1

@David es el modo de archivo. "w" significa escribir en un archivo. Si no especifica esto, tendrá el valor predeterminado de "rb" (modo binario de solo lectura) y obtendrá un error al intentar agregar a su archivo csv. Consulte http://www.ruby-doc.org/core-1.9.3/IO.html para obtener una lista de modos de archivos válidos en Ruby. –

+12

Gotcha. Y para futuros usuarios, si desea que cada iteración no sobrescriba el archivo csv anterior, use la opción "ab". –

2

luchando con esto a mí mismo. Esta es mi opinión:

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

Por cierto, tenga cuidado con las matrices multidimensionales en la palanca de JRuby. '[% w (su matriz),% w (va aquí)]' no se verá bonita. https://github.com/pry/pry/issues/568 –

27

Tengo esto a una sola línea.

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" 

Haz todo lo anterior y guardar en un archivo CSV, en una línea.

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

NOTA:

para convertir una base de datos de registro activo a csv sería algo como esto creo

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

Hmm @tamouse, que lo esencial es algo confuso para mí sin leyendo la fuente csv, pero genéricamente, suponiendo que cada hash en su matriz tiene el mismo número de pares k/v & que las claves son siempre las mismas, en el mismo orden (es decir, si sus datos están estructurados), esto debería hacer la escritura:

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 

Si los datos no está estructurado esto, obviamente, no va a funcionar

+0

Saqué un archivo CSV usando CSV.table, realicé algunas manipulaciones, me deshice de algunas columnas, y ahora quiero desplegar de nuevo la matriz resultante de Hashes como CSV (realmente delimitado por tabuladores). ¿Cómo? https://gist.github.com/4647196 – tamouse

+0

hmm ... esa esencia es algo opaca, pero se le da una variedad de hashes, todos con el mismo número de pares k/v y las mismas claves, en el mismo orden ... –

+0

Gracias, @boulder_ruby. Que funcionará. Los datos son una tabla de censo, y esa idea es bastante opaca al mirar hacia atrás. :) Básicamente, se extraen ciertas columnas de la tabla del censo original en un subconjunto. – tamouse

7

Sobre la base de @ boulder_ruby respuesta, esto es lo que estoy buscando , suponiendo que us_eco contiene la tabla CSV como desde mi punto de vista.

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 

Actualizado lo esencial en https://gist.github.com/tamouse/4647196

13

Si alguien está interesado, aquí están algunos de una sola línea (y una nota sobre la pérdida de información de tipo en 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 pierde toda la información de tipo, puede usar JSON para conservar información de tipo básico o ir a YAML detallado (pero más fácilmente editable por humanos) para conservar toda la información de tipo; por ejemplo, si necesita tipo de fecha, que se convertiría en cadenas en CSV & JSON.

10

Si tiene una matriz de matrices de datos:

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

entonces usted puede escribir a un archivo con el siguiente, que creo que es mucho más simple:

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