Apache POI というEXCELを操作するためのJava用のライブラリですが、
この記事では、Kotlinで使う場合の例を記載していきます。
Gradleセットアップ
2021年11月時点でのApache POIの最新は、5.1.0です。
build.gradle.kts
plugins{
kotlin("jvm")version"1.5.31"
}
repositories{
mavenCentral()
}
dependencies{
// Apache POI
implementation("org.apache.poi:poi:5.1.0")
implementation("org.apache.poi:poi-ooxml:5.1.0")
testImplementation("org.junit.jupiter:junit-jupiter:5.8.1")
testImplementation("org.junit.jupiter:junit-jupiter-params:5.8.1")
}
tasks.test{
useJUnitPlatform()
}
build.gradle.ktsに書かないといけない必須のdependenciesは、実は、implementation("org.apache.poi:poi-ooxml:5.1.0")
くらいです。
コアライブラリは、依存ライブラリになってるので、特に指定しなくても暗黙的に読み込まれすが、一応上記のように書いています。
基本の使い方
最低限の記述で、エクセルファイルを作成してみましょう。
import org.apache.poi.ss.usermodel.WorkbookFactory
import java.io.File
fun main() {
// 空のWorkbookインスタンスを作成
// 引数はxssf形式かどうか (XSSFが最新のエクセルバージョン形式です)
// closeを忘れないように、use拡張関数を使う
WorkbookFactory.create(true).use { wb ->
// シートの作成
// 作成とともにワークブックインスタス内にシートが追加されている。
val sheet = wb.createSheet("サンプルシート1")
// 行の作成 (ゼロインデックス: 1行目がゼロ)
val row = sheet.createRow(0)
// セルの作成 (ゼロインデックス: A列がゼロ)
val cell = row.createCell(0)
// セルに値を書き込み
cell.setCellValue("セルの値")
// ファイルへ書き込み
// OutputStreamのインスタンスへ書き込む
wb.write(File("sample.xlsx").outputStream())
}
}
作成したエクセルファイルを開いてみるとこんな感じ。
ワークブック操作
ワークブックインスタンスの取得には、WorkbookFactory.create
メソッドを使います。
いくつかのオーバーロードメソッドがあり、FileやInputStreamから読み込んでインスタンスを作成することや、先の例のように空ワークブックを作成することができます。
また、このインスタンスは、Closeable
なので、use拡張関数をつかって、自動closeするようにしておくと良いです。(Javaならtry-with-resourceで)
Fileインスタンスから作成して編集した場合、closeのタイミングで上書き保存となるので注意が必要です。
ワークブックの作成、読み取りと保存。
// 空のワークブック作成
WorkbookFactory.create(true).use { wb ->
// TODO: ワークブック操作
}
// 既存ファイルの読み込みと終了時に書き込み
// useスコープを抜けるタイミング、close時に上書き保存されます。
WorkbookFactory.create(File("sample.xlsx")).use { wb ->
// TODO: ワークブック操作
}
// 既存ファイルの読み込み。(読み取り専用)
// 第三引数に、readOnlyかどうかをBooleanで指定できます。
// ※close時に上書き保存はされません。
WorkbookFactory.create(File("sample.xlsx"), null, true).use { wb ->
// TODO: ワークブック操作
}
// 既存ファイルの読み込み。(読み取り専用)
// InputStream経由の場合、エクセルファイルは読み込み時にすべてオンメモリ展開される。
// ※close時に上書き保存はされません。
WorkbookFactory.create(File("sample.xlsx").inputStream()).use { wb ->
// TODO: ワークブック操作
}
保存は、Fileインスタンスを渡した場合は、closeの際に上書き保存されますが、
それ以外で保存したい場合は、writeメソッドをコールします。
// ファイルへ書き込み
// OutputStreamのインスタンスへ書き込む
wb.write(File("sample.xlsx").outputStream())
シート操作
Sheetインスタンスの取得と追加
// 既存のシートを取得
val sheet = wb.getSheet("サンプルシート1")
// 存在しないシートは、nullを返してくるので、
// 例外を出さずにnull判定したい場合は、Sheet? で変数宣言して扱うとよい。
val sheetOrNull: Sheet? = wb.getSheet("サンプルシート2")
if (sheetOrNull != null) {
println(sheetOrNull.sheetName)
}
// イテレーションで取得
for (s in wb.sheetIterator()) {
println(s.sheetName)
}
// 新規シートを作成
val newSheet = wb.createSheet("追加シート")
注意が必要なのは、存在しないシート名でインスタンス取得しようとする場合です。
Kotlinだと、Workbook#getSheet
メソッドの返り値は Sheet!
のようなプラットフォーム型(Null許容情報がない型)になります。
シートが存在しない場合には実際にnullが返るので、そのような状況を扱う場合は、Sheet? と変数宣言してnull判定してから利用すると良いでしょう。
セル操作
行とセルの取得と作成、セルの値取得と設定。
// 行の取得(ない場合はnullを返す)
val row = sheet.getRow(0)
// セルの取得(ない場合はnullを返す)
val cell = row.getCell(0)
// セルの値を取得(文字列型で)
val value = cell.stringCellValue
println(value)
// A2 のRowとCellを作成
val cellA2 = sheet.createRow(1).createCell(0)
cellA2.setCellValue("A2の値")
// CellUtil#getRow と、CellUtil#getCell は、なかったらcreateしてくれる。
val cellA3 = CellUtil.getCell(CellUtil.getRow(2, sheet), 0)
cellA3.setCellValue("A3の値")
Cellのインスタンスを取得するには、まずRowのインスタンスが必要になります。
どちらもSheetインスタンスから、Sheet#getXXXで取得できますが、作成されるまではnullが返ります。
作成するには、Sheet#createXXXを使用しましょう。
CellUtilクラスには、nullだったら内部でcreateしてくれる便利メソッドが用意されていますが、Kotlinで使うには少しまどろっこしいです。
下記のような拡張関数を用意しておくと便利です。
/**
* Row, Cellがなかったらcreateして返す。
*/
fun Sheet.cell(rowIndex: Int, columnIndex: Int): Cell {
return CellUtil.getCell(CellUtil.getRow(rowIndex, this), columnIndex)
}
/**
* "A1"のようなアドレス指定で、セルを取得する。
* なかったらcreateして返す。
*/
fun Sheet.cell(cellRef: String): Cell {
val ref = CellReference(cellRef)
check(ref.sheetName == null) { "Specifying sheet name is unsupported" }
return this.cell(ref.row, ref.col.toInt())
}
CellReferenceには、シート名を含めた記述も出来ますが、
↑の拡張関数ではややこしくなるためサポートしないようにしています。
// 拡張関数を使ってセル取得 (row, col)
val cellB5 = sheet.cell(4, 1)
println(cellB5.stringCellValue)
// なじみのあるアドレス形式でセル取得
val cellA3 = sheet.cell("A3")
println(cellA3.stringCellValue)
セルの値
セルに値を設定するには、Cell#setCellValueを使用します。
設定した値によって、CELL_TYPEが固定されるので、取得時は正しいタイプで取得しないとnullや例外が発生します。
定義されているCELL_TYPEはこれだけあります。
- _NONE
- NUMERIC
- STRING
- FORMULA
- BLANK
- BOOLEAN
- ERROR
Cellの値を文字列で取得する。
// 先のほどの自作の拡張関数でCellインスタンス取得
val cell = sheet.cell("A1")
// FORMULAの場合、キャッシュされてるタイプで判断する。
val cellType = cell.cellType.let { if (it == CellType.FORMULA) cell.cachedFormulaResultType else it }
// STRINGタイプ以外なら空文字で取得
val str = when (cellType) {
CellType.STRING -> cell.stringCellValue ?: ""
else -> ""
}
println("ret:\"$str\"")
↑の例では、文字列以外のセルタイプの場合は、空文字で取得しています。
もし、NUMERICで設定されているセルを文字列で取得したい場合は、
when式にNUMERICのケースを追加して、cell.numericCellValue.toString()
とするのが良いでしょう。
FORMULAタイプは、エクセルの関数式がはいっているタイプになります。
計算済みの値がキャッシュされているので、キャッシュされているタイプを判断して取得することができます。
まとめ
エクセルをJavaから操作するためのライブラリ Apache POIについて、
Kotlinを使った簡単な例で紹介しました。
なかなか回りくどくて扱いづらいかもしれませんが、
Kotlinの拡張関数を定義すればラクに使うことができます。