エクセルファイルにアクセス
仕事でExcelファイルを使うのだが、いつも集計作業が手作業になりがち。
複雑な処理をしたいけど、マクロをいまから覚えたくもない。
Rubyでなら Enumerableのメソッドをチェーンして書きまくれるのに…といつも思って、
コピペしてテキストファイルにして split してから作業してました。
耐えられなくなって調べてみたら、Rubyにはwin32oleというライブラリがあり、
これを使うとExcelファイル等にRubyからアクセスできるようになる。
CygwinのRubyを使えばそのまま使える。
http://jp.rubyist.net/magazine/?0004-Win32OLE
でもこれを使うとなんだかメソッドが一般的すぎて使いにくい。
せっかく覚えても、そんなに必要になる機会が多いわけでもないので、すぐに忘れる。
使いたいときに忘れのでまた「るびま」を読みなおすということに。
3回くらい「るびま」を読んだのでこれはいかんと思って、
よく使う機能をまとめたモジュールを作った。
require 'win32ole' require 'enumerator' require 'time' require 'forwardable' module Excel def self.open(filename) excel = WIN32OLE.new('Excel.Application') books = excel.Workbooks.Open(filename) begin yield(Books.new(books)) ensure books.Close excel.Quit end end class Books include Enumerable def initialize(books) @books = books end def [](sheet) Sheet.new(@books.Worksheets(sheet)) end def each @books.Worksheets.each do |e| yield(Sheet.new(e)) end end end class Sheet include Enumerable extend Forwardable def initialize(sheet) @sheet = sheet end def [](x, y) Value.new(@sheet.Cells.Item(x, y)) end def each @sheet.UsedRange.Rows.each do |row| yield(row.Columns.enum_for(:each).collect {|c| Value.new(c)}) end end def_delegator :@sheet, :name end class Value def initialize(obj) @obj = obj @value = obj.Value if @value.is_a?(String) && @value =~ %r(\d{4}/\d\d/\d\d \d\d:\d\d:\d\d) @value = Time.parse(@value) end end attr_reader :value def comment comment = @obj.Comment comment && comment.Text end end end
以下は添付画像のようなExcelシートを操作するを例。
Book操作
「シート1」にアクセス
シート番号とシート名でのアクセスが可能
Excel.open("test.xls") do |books| books[1] end => #<Excel::Sheet:0x4986428 @sheet=#<WIN32OLE:0x4986440>> Excel.open("test.xls") do |books| books["シート1"] end => #<Excel::Sheet:0x497fa38 @sheet=#<WIN32OLE:0x497fa50>>
「シート」にマッチするシートの配列を取得
Enumerableをincludeしているので後述するSheetクラスを操作するコードを記述できる。
Excel.open("test.xls") do |books| books.select {|e| e.name =~ /1/} end => [#<Excel::Sheet:0x489a220 @sheet=#<WIN32OLE:0x489a268>>] # シート1
シート操作
「シート1」のB3取得
Excel.open("test.xls") do |books| books["シート1"][2, 3] end => #<Excel::Value:0x489a208 @obj=#<WIN32OLE:0x489a220>, @value="\212J\216n\216\236\215\217">
行ごとにイテレート
Excel.open("test.xls") do |books| books["シート1"].each do |_, name, start, elapse| puts "#{name.value} #{start.value} #{elapse.value}" end end テスト名 開始時刻 経過時間 (秒) test_foo Wed Oct 17 00:11:00 GMT+9:00 2007 60.0 test_bar Wed Oct 17 00:13:10 GMT+9:00 2007 400.0 test_baz Wed Oct 17 00:21:20 GMT+9:00 2007 120.0
セルに付随するコメントを取得
Excel.open("test.xls") do |books| puts books["シート1"][3, 4].comment end "再計測が必要かも"