Tablica wyjściowa do CSV w Ruby


185

Łatwo jest wczytać plik CSV do tablicy za pomocą Ruby, ale nie mogę znaleźć dobrej dokumentacji na temat zapisywania tablicy w pliku CSV. Czy ktoś może mi powiedzieć, jak to zrobić?

Używam Ruby 1.9.2, jeśli to ma znaczenie.


3
Odpowiedź, którą masz, jest świetna, ale zachęcam do nieużywania CSV. Jeśli nie masz tabulatorów w danych, łatwiej jest sobie radzić z plikami rozdzielanymi tabulatorami, ponieważ nie wymagają one aż tak cholernego cytowania i ucieczki. Jeśli musisz użyć CSV, oczywiście są to przerwy.
Bill Dueber

8
@Bill, moduł CSV starannie obsługuje pliki rozdzielane tabulatorami, a także rzeczywiste pliki csv. Opcja: col_sep pozwala określić separator kolumn jako „\ t” i wszystko jest w porządku.
tamouse

1
tutaj jest więcej informacji o dokumentach CSV docs.ruby-lang.org/en/2.1.0/CSV.html
veeresh yh

Używam plików .tab z tym modułem, ponieważ przypadkowo otwarcie tego w Excelu może zepsuć kodowanie…
MrVocabulary

Odpowiedzi:


326

Do pliku:

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

Do ciągu:

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

Oto aktualna dokumentacja dotycząca CSV: http://ruby-doc.org/stdlib/libdoc/csv/rdoc/index.html


1
@David to tryb plików. „w” oznacza zapis do pliku. Jeśli nie określisz tego, domyślnie będzie to „rb” (tryb binarny tylko do odczytu) i wystąpi błąd podczas próby dodania do pliku csv. Zobacz ruby-doc.org/core-1.9.3/IO.html, aby uzyskać listę prawidłowych trybów plików w Rubim.
Dylan Markow

15
Gotcha A dla przyszłych użytkowników, jeśli chcesz, aby każda iteracja nie zastępowała poprzedniego pliku csv, skorzystaj z opcji „ab”.
boulder_ruby

1
Zobacz tę odpowiedź dla trybów
Nick

38

Mam tylko jedną linię.

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" 

Wykonaj wszystkie powyższe czynności i zapisz w pliku csv w jednym wierszu.

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

UWAGA:

Myślę, że konwersja bazy danych aktywnych rekordów do csv byłaby czymś takim

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

Hmm @tamouse, ta treść jest dla mnie nieco myląca bez czytania źródła csv, ale ogólnie, zakładając, że każdy skrót w twojej tablicy ma tę samą liczbę par k / v i że klucze są zawsze takie same, w tej samej kolejności (tj. jeśli twoje dane są uporządkowane), powinno to zrobić:

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

Jeśli twoje dane nie są uporządkowane, to oczywiście nie zadziała


Wciągnąłem plik CSV za pomocą CSV.table, wykonałem pewne manipulacje, pozbyłem się niektórych kolumn, a teraz chcę ponownie buforować wynikową tablicę skrótów jako CSV (naprawdę rozdzieloną tabulatorami). Jak? gist.github.com/4647196
tamouse

hmm ... ta treść jest nieco nieprzejrzysta, ale biorąc pod uwagę tablicę skrótów, wszystkie z tą samą liczbą par k / v i tymi samymi kluczami, w tej samej kolejności ...
boulder_ruby

Dzięki, @boulder_ruby. To zadziała. Dane są tabelą spisu ludności, a ta istota jest raczej nieprzejrzysta, patrząc na nią. :) Zasadniczo wyodrębnia niektóre kolumny z oryginalnej tabeli spisu ludności do podzbioru.
tamouse

3
Nadużywasz injecttutaj, naprawdę chcesz z niego korzystać map. Ponadto nie musisz przekazywać pustego ciągu join, ponieważ jest to ustawienie domyślne. Możesz więc zmniejszyć to jeszcze bardziej:rows.map(&CSV.method(:generate_line).join
iGEL

1
Drugi przykład jest zbyt skomplikowany, ponieważ biblioteka CSV jest dość potężna. CSV.generate(headers: hsh.first&.keys) { |csv| hsh.each { |e| csv << e } }generuje równoważny CSV.
Amadan

28

Jeśli masz tablicę tablic danych:

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

Następnie możesz zapisać to do pliku w następujący sposób, który moim zdaniem jest znacznie prostszy:

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

20

Jeśli ktoś jest zainteresowany, oto kilka linijek (i uwaga na temat utraty informacji o typie w 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

Uwaga: CSV traci wszystkie informacje o typie, możesz użyć JSON, aby zachować podstawowe informacje o typie, lub przejść do pełnego (ale łatwiejszego do edycji przez człowieka) YAML, aby zachować wszystkie informacje o typie - na przykład, jeśli potrzebujesz typu daty, który stałby się ciągi w CSV i JSON.


9

Opierając się na odpowiedzi @ boulder_ruby, właśnie tego szukam, zakładając, że us_ecozawiera tabelę CSV z mojej treści.

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

Zaktualizowano treść na https://gist.github.com/tamouse/4647196


Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.