日々常々

ふつうのプログラマがあたりまえにしたいこと。

POIを使ってみる/書式の扱い

いろんな基本的なことはすっ飛ばして、書式について書くことにします。バージョンは3.6で。
書式というと、表計算ソフトとしては数値のフォーマットが最重要のはずなのですが、現在日本でExcelが使われる中では、フォーマットより、フォント(サイズや色も含む)や背景色、罫線なんかの比重が非常に高くなっています。と言う事で、今回のポイントもそこになります。

さて、POIではExcelの「セルの書式設定」で扱える項目は、殆どがCellStyleで表現されます。そしてこのCellStyleは、Cellの持ち物ではなくWorkbookの持ち物です。これはPOIだからこそという話ではなく、作成されるファイルを見る限り、MicrosoftExcelも内部ではそんな感じで扱っているようです。Cellに持つほうが自然な気はしますし、実際の操作上でもあたかもCellが持っている情報のように扱われます。Workbookに持っているのは、CellStyleが結構大きな情報だから使いまわす為なのか、もっとほかの事情があったからなのかは不明ですけど、とにかくそういうことになっているので、書式を編集する場合は気をつけなければなりません。

さて、WorkbookからCellを取得する方法について何も書いていなかったのですが、この辺りは突っ切ってしまって、シート名「Sheet1」のA1セルの書式を取得することにします。bookはWorkbookを読み込んだフィールドです。

Sheet sheet = book.getSheet("Sheet1");
Row row = sheet.getRow(0);
Cell cell = row.getCell(0);
CellStyle cs = cell.getCellStyle();

これでcsフィールドはA1セルの書式になります。と書くと誤解を生むかも。A1セルのCellStyleはcsだけれども、csはA1セル専用のCellStyleではありません。先に書いたとおり、CellStyleはWorkbookの持ち物であり、Cell#getCellStyleの実際の挙動は、Cellが持っている書式情報のindexで、Workbook#getCellStyleAt(index)を呼び出す感じのことをしています。

上で書いた「気をつけなければならない」点は、1セルの書式を変更したいからって、うっかりCellから取得したCellStyleの属性を変更しようものなら、実際変更したいセル以外のセルもすべて書式が変更される可能性があることです。書式を変更したい場合は、安全策をとるならば、Workbook#createCellStyleで新しく作成したCellStyleをCell#setCellStyleで適用することです。複製にはCellStyle#cloneStyleFromが使えます。以下のような感じになります。この例では、Workbook内に同じ情報のCellStyleが2つ定義されることになります。

CellStyle source = cell.getCellStyle();
CellStyle cs = book.createCellStyle();
cs.cloneStyleFrom(source);
cell.setCellStyle(cs);

そんなことをしていると、作成したファイルをMicrosoftExcelで開いた際に「表示形式を追加できません」というエラーが出る可能性があります。これはExcelが扱える書式数の上限が約4,000であるためです。1セル毎にCellStyleを作成していたら、4,000なんて意外とあっさり超えるものです。エラーの詳細は以下のサポートページをごらんください。
Excel で "別のセルの書式が多すぎます。" エラー メッセージが表示される