Dizi Ruby'de CSV'ye çıktı


185

Bir CSV dosyasını Ruby ile bir diziye okumak yeterince kolaydır, ancak CSV dosyasına bir dizinin nasıl yazılacağına dair iyi bir belge bulamıyorum. Biri bana bunun nasıl yapılacağını söyleyebilir mi?

Bu önemliyse Ruby 1.9.2 kullanıyorum.


3
Cevabınız harika, ama size CSV kullanmama izin vereyim. Verilerinizde sekmeler yoksa, sekmeyle ayrılmış dosyalarla uğraşmak çok daha kolaydır, çünkü çok garip 'alıntılama ve kaçış vb. İçermezler. CSV kullanmanız gerekiyorsa, elbette, onlar molalar.
Bill Dueber

8
CSV modülü, sekmeyle ayrılmış dosyaları ve gerçek csv dosyalarını düzgün bir şekilde işler. : Col_sep seçeneği, sütun ayırıcıyı "\ t" olarak belirtmenizi sağlar ve her şey yolundadır.
tamouse

1
CSV hakkında daha fazla bilgi docs.ruby-lang.org/tr/2.1.0/CSV.html
veeresh yh

Bu modülle .tab dosyalarını kullanmak benim yaptığım şeydir, çünkü yanlışlıkla Excel'de açmak kodlamayı
bozar

Yanıtlar:


326

Bir dosyaya:

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

Bir dizeye:

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

CSV hakkındaki mevcut belgeler şunlardır: http://ruby-doc.org/stdlib/libdoc/csv/rdoc/index.html


1
@David bu dosya modu. "w" bir dosyaya yazmak anlamına gelir. Bunu belirtmezseniz, varsayılan olarak "rb" (salt okunur ikili mod) olur ve csv dosyanıza eklemeye çalışırken bir hata alırsınız. Ruby'deki geçerli dosya modlarının listesi için ruby-doc.org/core-1.9.3/IO.html adresine bakın .
Dylan Markow

15
Anladım. Ve gelecekteki kullanıcılar için, her yinelemenin önceki csv dosyasının üzerine yazmamasını istiyorsanız, "ab" seçeneğini kullanın.
boulder_ruby

1
Ruby File IO Modları için bu cevaba bakın: stackoverflow.com/a/3682374/224707
Nick

38

Bunu sadece bir satıra indirdim.

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" 

Yukarıdakilerin tümünü yapın ve bir satırda bir csv'ye kaydedin.

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

NOT:

Aktif bir kayıt veritabanı csv dönüştürmek için bence böyle bir şey olurdu

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

Hmm @tamouse, bu gist csv kaynağını okumadan benim için biraz kafa karıştırıcı, ancak genel olarak, dizinizdeki her karmanın aynı sayıda k / v çiftine sahip olduğunu ve anahtarların her zaman aynı, aynı sırada olduğunu varsayarak (yani eğer verileriniz yapılandırılmışsa) bu işlem aşağıdaki gibidir:

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

Verileriniz yapılandırılmamışsa, bu kesinlikle işe yaramaz


CSV.table kullanarak bir CSV dosyası çektim, bazı manipülasyonlar yaptım, bazı sütunlardan kurtuldum ve şimdi ortaya çıkan Hashes Array'ı CSV (gerçekten sekmeyle ayrılmış) olarak tekrar biriktirmek istiyorum. Nasıl? gist.github.com/4647196
tamouse

hmm ... bu gist biraz opak, ama aynı sayıda k / v çifti ve aynı anahtarları aynı sırayla, bir dizi karma verilen ...
boulder_ruby

Teşekkürler, @boulder_ruby. İşe yarayacak. Veriler bir nüfus sayımı tablosu ve bu özgeçmiş geriye bakarken oldukça opak. :) Temelde orijinal nüfus sayımı tablosundan belirli sütunları bir alt kümeye ayıklar.
tamouse

3
injectBurada yanlış kullanıyorsunuz , gerçekten kullanmak istiyorsunuz map. Ayrıca, joinvarsayılan olarak boş bir dize iletmeniz gerekmez . Böylece bunu daha da daraltabilirsiniz:rows.map(&CSV.method(:generate_line).join
iGEL

1
CSV kütüphanesi oldukça güçlü olduğu için ikinci örneğiniz aşırı karmaşık. CSV.generate(headers: hsh.first&.keys) { |csv| hsh.each { |e| csv << e } }eşdeğer bir CSV oluşturur.
Amadan

28

Bir dizi veri diziniz varsa:

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

Sonra bunu çok daha basit olduğunu düşündüğüm bir dosyaya yazabilirsiniz:

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

20

Eğer ilgilenen varsa, işte bazı tek satırlar (ve CSV'de tür bilgisi kaybına ilişkin bir not):

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

Not: CSV tüm tür bilgilerini kaybeder, temel tür bilgilerini korumak için JSON'u kullanabilir veya tüm tür bilgilerini korumak için ayrıntılı (ancak daha kolay insan tarafından düzenlenebilir) YAML'ye gidebilirsiniz - örneğin, tarih türüne ihtiyacınız varsa, CSV ve JSON dizeleri.


9

@ Boulder_ruby'nin cevabına dayanarak, bu benim özümden olduğu us_ecogibi CSV tablosunu içerdiğini varsayarak arıyorum .

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

En özünü Güncelleme https://gist.github.com/tamouse/4647196


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.