2011年6月1日水曜日

[Rails3] Spreadsheetを使う(その2)

実行環境:
Rails 3.0.7
Spreadsheet 0.6.5.4

Spreadsheetを使う(その2):Excelファイルを読む

今のバージョンでは、読み込めるExcelファイルはExcel2003形式(.xls)だけのようで、Excel2007形式(.xlsx)は読み込めません。

Excelファイルを開く
book = Spreadsheet.open('public/test.xls', 'rb')
ファイル名の起点はアプリケーションのルートディレクトリ(#{Rails.app})になります。したがってファイル名はルートディレクトリからの相対パスで指定します。

ワークシートを開く
ワークシートは0から始まる番号で指定するか、ワークシート名で指定します。
番号開く(引数がInteger)
sheet = book.worksheet(0)
名前で開く(引数がString)
sheet = book.worksheet("sheet1")

存在しないシートを指定すると nil が返ります。
if book.worksheets(500).nil? then
  ...
end

ワークブックに含まれるワークシートすべてにアクセスするには worksheetsメソッドを使います
book.worksheets.each do |sheet|
  ...
end

データが空っぽのワークシートをスキップするなら…
book.worksheets.select{|s| s.row_count>0 }.each do |sheet| ... end


シート内の各セルのデータを取得する
特定のセルの値にアクセスする方法。行番号(i)、列番号(j)はいずれも0から始まります。つまり"A1"セルの座標(i,j)は(0,0)、"B1"セルなら(1,0)です。
セルの座標を直接指定する
cell = sheet[i,j] cell = sheet.cell(i,j)
行クラスのインスタンスを取得し、そのj番目を指定
row = sheet.row(i) cell = row[j]
列クラスのインスタンスを取得し、そのi番目を指定
column = sheet.column(j) cell = column.to_a[i] # Columnクラスには[]が定義されていない

eachを使って各セルの値を取得する
sheet.each do |row|
  row.each do |cell|
    value = cell
  end
end
cellにはセルの内容によって
・Stringクラス
・Floatクラス(整数の場合もfloatになる)
・Dateクラス
などのインスタンスが入ります。数字や日付の場合でもExcel上でセルの値の頭に「'」をつけておくStringとして取得します。表示形式を文字列にしても変わりません。
セルが空白の場合は Nil が返ります。
数式の場合はSpreadsheet::Formulaクラスのインスタンスが入ります。cell.valueで最後に計算された値が取得できます。

各セルの値を取得する(数式の入ったセルにも対応)
sheet.each do |row|
  row.each do |cell|
    value = cell.instance_of?(Spreadsheet::Formula) ? cell.value : cell
  end
end

sheet.each、row.each の走査範囲
シート内のデータを走査する場合に、値の入っていない行を65535行も走査するとしたら無駄ですね。でも、上記の sheet.each や row.each ではちゃんとデータが入っている範囲だけを走査してくれます。たとえば最初の行がどのセルにも値が入っていない空行であれば2行目から読みます。列方向も同様です。

ということは sheet.each で現れる最初の行が1行目とは限らない、ということになります。
行/列の番号を気にする場合はカウンター変数を使った方がいいでしょう。Worksheet.dimensions メソッドでシート内のデータが入っているセル範囲が取得できます。
first_used_row, first_unused_row, first_used_col, first_unused_col = sheet.dimensions

(first_used_row...first_unused_row).each do |i|
  row = sheet.row(i)
  (first_used_col...first_unused_col).each do |j|
    cell = row[j]
  end
end
ちなみに、データが入っていない行/列であっても書式が設定されていると有効範囲として認識されます。

0 件のコメント:

コメントを投稿