5
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Excelファイル(xls, xlsx)をRubyで2次元配列として読み書きする

Last updated at Posted at 2015-02-26

WIN32APIをそのまま触るとつらかったのでラッパーにしてみました。Windows専用です。

Excelファイルのシート単位で2次元配列として読みだして、慣れてるオブジェクト操作をして、出来上がったものをシート単位で書きだそう、という考えを適用しています。2次元配列ならみんな慣れてるので楽です。

WIN32OLEを直接触ることで他の操作も可能です。

なお、ExcelのWIN32APIのラッパーはWrapExcelというGemがあります。

使い方

インスタンス生成時にファイルを指定します。相対パスでも絶対パスでも可能です。
存在しないファイルを指定した場合には、新規作成となります。

末行に値を追加する
xl = ExcelTable.new('Book1.xlsx')

# Sheet1を2次元配列として読み出す
# シートの指定は数字でも可能
table = xl.read("Sheet1") 
table # => [["tes"]]

# 2次元配列を編集する
row = ["hoge", "ふが", Time.now, true, false, nil, 33, 3.14 ] # これらが使用可能なオブジェクト
table << row # 1行追記する

# 2次元配列をSheet1に反映
xl.write(table, "Sheet1") 

# 変更を保存して終了
xl.close 

実行結果↓
キャプチャ.PNG

closeを忘れたり例外によって停止した場合、at_exitによってExcelを「変更を保存せずに終了」します。

コード

excel_arrays.rb
class ExcelTable
  def initialize(file_path)
    require 'win32ole'
    @excel = WIN32OLE.new('Excel.Application')
    @fp = WIN32OLE.new('Scripting.FileSystemObject').GetAbsolutePathName(file_path)
    # cf. http://d.hatena.ne.jp/maluboh/20070709/p1

    @wbook = if File.exist?(@fp)
      @excel.Workbooks.Open(@fp)
    else
      @excel.Workbooks.Add
    end

    @allow_type = [ String, Numeric, Time, TrueClass, FalseClass, NilClass ]
    raise "\n#{file_path} は既に開かれているか読み取り専用です" if @wbook.Readonly

    # 例外時でもちゃんと閉じる
    # (不十分な実装)@excelが既に閉じているかどうかを調べるWIN32API関数?
    at_exit{ begin close(false) rescue nil end }
  end
  attr_accessor :excel, :wbook, :allow_type, :fp

  def read(sheet_key=1)
    sheet2table(@wbook.sheets[sheet_key])
  end

  def write(table, sheet_key=1)
    value_type_checking = table.all?{|a| a.all?{|v| @allow_type.any?{|type|v.kind_of?(type)} } }
    raise "非対応の型が含まれています" unless value_type_checking
    table2sheet(table, @wbook.sheets[sheet_key])
  end

  def close(is_save=true)
    if is_save
      @excel.DisplayAlerts = false
      @wbook.SaveAs(@fp)
    end

    @wbook.Close
    @excel.Quit
  end

  private
  def sheet2table(sheet)
    sheet.UsedRange.Rows.each.map{|row|
      a = row.Value
      a[0].class == Array ? a[0] : [a]
    }
  rescue
    []
  end

  def table2sheet(table, sheet)
    table.each.with_index(1) do |arr, i|
      arr.each.with_index(1) do |val, j|
        sheet.Rows[i].Columns[j] = val
      end
    end
  end
end

おことわり

数式に対応していません。「=A1+A2」で計算結果が3.0のセルがあるとき、このラッパーはそれを3.0として読み込みます。さらに、ExcelTable#writeはシート上の数値を全て上から値貼り付けするので、もとの「=A1+A2」という情報は失われます。

存在しないシートを指定したなど、例外処理してない例外はmethod_missingになります。

5
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?