CSV形式のファイルのインポート/エクスポートについて

CSV形式とは?

1単語ずつカンマで区切られていて、1行ずつ情報が並んでいます。また、テキストデータとは文字のみのデータのことを指し、ワードやエクセルのように画像が入っていたり、文字サイズや色の装飾などがされていないプレーンな文字データのことを言います。通常のテキストデータの拡張子は「.txt」ですがCSVの場合は「.csv」となります。

# CSV形式の例
氏名,年代,性別,住所
山田 花子,30代,女性,東京都豊島区
田中 太郎,50代,男性,和歌山県有田郡

CSVは各ソフトウェアが共通で対応している形式のため、異なるソフトウェア間で簡単にデータの移行が可能になっています。

library csv

csv形式のデータを扱うには、csvライブラリが必要です。

そのため、ライブラリを取り込んで、使えるように設定が必要です。

require 'csv'
~省略~

読み込み

# CSVライブラリを読み込む
require "csv"

csv_text = <<~CSV_TEXT
  Ruby,1995
  Rust,2010
CSV_TEXT

IO.write "sample.csv", csv_text

# ファイルから一行ずつ
CSV.foreach("sample.csv") do |row|
  p row
end
# => ["Ruby", "1995"]
#    ["Rust", "2010"]

# ファイルから一度に
p CSV.read("sample.csv")
# => [["Ruby", "1995"], ["Rust", "2010"]]

# 文字列から一行ずつ
CSV.parse(csv_text) do |row|
  p row
end
# => ["Ruby", "1995"]
#    ["Rust", "2010"]

# 文字列から一度に
p CSV.parse(csv_text)
# => [["Ruby", "1995"], ["Rust", "2010"]]

書き込み

# CSVライブラリを読み込む
require 'csv'

# ファイルへ書き込み
CSV.open("path/to/file.csv", "wb") do |csv|
  csv << ["row", "of", "CSV", "data"]
  csv << ["another", "row"]
  # ...
end

# 文字列へ書き込み
csv_string = CSV.generate do |csv|
  csv << ["row", "of", "CSV", "data"]
  csv << ["another", "row"]
  # ...
end

1行変換

# CSVライブラリを読み込む
require 'csv'

csv_string = ["CSV", "data"].to_csv   # => "CSV,data"
csv_array  = "CSV,String".parse_csv   # => ["CSV", "String"]

現場Rails p312[csv出力する(エクスポート)]

app/models/task.rb

#タスクデータをCSVファイルとして出力する方法
class Task < ApplicationRecord

 # どの属性をどの順番で出力するかを定義してcsv_attributesメソッドで要素に属性名の文字列を持つ配列を作成します。
  def.csv_attributes
    ["name", "description", "created_at", "updated_at"]
  end

  # CSV形式でインスタンスの中身を出力できるようにします。
  def self.generate_csv
        # CSV.geterateでCSVデータの文字列を生成する
    CSV.generate(headers: true) do |csv|

      # headers: trueはデータベースの一番上の行(ヘッダー)をCSVのレコードのタイトルをとして指定します。
      # csvに上記で定義したcsv_attributesを一行目として代入します。
      csv << csv_attributes
      all.each do |task| # 省略前は、Task.all.each do |task|
        # Task.allでタスクを全件取得。その後、eachでtaskを一つ一つ取り出してtaskの値を代入していきます。
        csv << csv_attributes.map{ |attr| task.send(attr) }
      # mapはcsv_attributesの配列の中身全てに対して処理を行います。
            # この時、|attr|には["name", "description", "created_at", "updated_at"]が順番に入って処理に渡されます。
      # task.sendは引数に指定したメソッドを文字列で返します。
      # 文字列として返った属性名の中身をCSVに追加します。
      end

    end

  end

private
~省略~

end
  • <<は配列の末尾に要素を追加するメソッド

Array#<< (Ruby 3.0.0 リファレンスマニュアル)

  • map() メソッドは、与えられた関数を配列のすべての要素に対して呼び出し、指定した処理の結果を要素にした新しい配列を生成します。
# 配列の要素をすべて 3 倍にする→ その結果を新しい配列として生成する。
# 配列pの要素を順番に変数|n|に取り出してきて、そのnを使って、{ブロック内の後ろの処理を行う}。
# n * 3をした結果を新たな要素として配列を作成する。
p [1, 2, 3].map {|n| n * 3 }  # => [3, 6, 9]

Array#collect (Ruby 3.0.0 リファレンスマニュアル)

  • sendメソッドは、引数として渡されたメソッド(文字列、シンボル、変数として渡された)を実行し、その実行結果を返します。つまり、レシーバの持っているメソッドを呼び出してくれるメソッドです。 ブロック付きで呼ばれたときはブロックもそのまま引き渡します。
class User
  def name
    puts "taro"
  end
end

user = User.new
#定義したメソッドを呼び出す
user.name  # => taro

# sendを使った書き方
user.send(:name) # => taro
user.send("name")  # => taro

Object#send (Ruby 3.0.0 リファレンスマニュアル)

  • CSV.generateメソッドは、与えられた文字列をラップして CSV のオブジェクトとしてブロックに渡します。ブロック内で CSV オブジェクトに行を追加することができます。ブロックを評価した結果は文字列を返します。
generate(str = "CSVに変換したい文字列", options = Hash.new) {|csv| ... } -> String

CSV.generate (Ruby 3.0.0 リファレンスマニュアル)

app/controller/tasks_controller.rb

# コントローラに異なるフォーマットでの出力機能を追加する
def index
  ~ 略 ~
  respond_to do |format|
      # formatがHTMLとしてアクセスされた場合
    format.html 
        # formatがCSVでアクセスされた場合
    # send_dataメソッドを使って、レスポンスを送り出します。
        # モデルで定義したgererate_csvメソッドを使って、タスクデータをCSV形式にしたものをレスポンスとしています。
    format.csv { send_data @tasks.generate_csv, filename: "sample.csv"}
  end
end

参照

CSVファイルとは何?何のために使うの?

library csv

【Ruby】よく使う、CSVライブラリを使ったCSV操作 - Qiita

Array#<< (Ruby 3.0.0 リファレンスマニュアル)

Array#collect (Ruby 3.0.0 リファレンスマニュアル)

Object#send (Ruby 3.0.0 リファレンスマニュアル)

【Ruby on Rails】sendメソッドのいろんな書き方 - Qiita

Array.prototype.map() - JavaScript | MDN

CSV.generate (Ruby 3.0.0 リファレンスマニュアル)