LoginSignup
1
0

More than 1 year has passed since last update.

RowやCellをイテレータで処理する場合は未定義の行やセルに気を付ける

Last updated at Posted at 2021-05-22

発生した問題

Apache POIを利用するシステム開発プロジェクトでは「指定したシートを読み込み、2次元配列に変換する」というようなAPIを作成することがあります。自分がかかわっているプロジェクトでもそのようなAPIが提供されており、おおよそ以下のように実装されていました (読み込ませるシートはすべて文字列になっている想定)

try (Workbook book = WorkbookFactory.create(Paths.get("target-book.xlsx").toFile())) {
    Sheet sheet = book.getSheet("target-sheet");

    List<List<String>> lists = new ArrayList<>();
    for (Row row : sheet) {
        List<String> list = new ArrayList<>();
        for (Cell cell : row) {
            list.add(cell.getStringCellValue());
        }
        lists.add(list);
    }

    doSomeThing(lists);
}

さてこの実装は少なくともこのAPIを提供したチームが公表していた仕様を満たしていませんでした。たとえばこのAPIを使って以下のようなExcelを読み込ませたとします。

image.png

このシートは以下のような二次元配列に変換されます。

[A1, B1, C1, D1, E1], 
[B2, D2],  
[A4, B4, C4, D4, E4]

しかしAPI提供チームがうたっていた仕様では、このExcelシートは以下のように変換されるはずでした。

[A1, B1, C1, D1, E1], 
[  , B2, ,   D2], 
[], 
[A4, B4, C4, D4, E4]

つまり、空の行や空のセルについても適切に処理するというのがAPI仕様でしたが、実装はそのようになっていませんでした。

原因と対策

原因はRowCellの読み込みにイテレータを利用していることです。SheetIteratable<Row>を、 RowIterable<Cell>をそれぞれ実装していますが、これらを利用すると、未定義の行や未定義のセルについては、存在しないものとしてスキップします。上述の例でいうと、3行目やA2セルやA3セルは、Apache POI上、未定義として扱われてしまい、イテレータ読み込みではないものとしてスキップされてしまいます。

こういう空の行や空のセルをうまくハンドリングするには、Sheet.getRowRow.getCellを使う必要があります。今回の場合は以下のように実装を修正しました。

try (Workbook book = WorkbookFactory.create(Paths.get("target-book.xlsx").toFile())) {
    Sheet sheet = book.getSheet("target-sheet");

    List<List<String>> lists = new ArrayList<>();
    for (int i = 0; i <= sheet.getLastRowNum(); i++) {
        Row row = sheet.getRow(i);
        List<String> list = new ArrayList<>();

        int len = (row == null) ? 0 : row.getLastCellNum();
        for (int j = 0; j < len; j++) {
            Cell cell = row.getCell(j);
            list.add((cell == null) ? "" : cell.getStringCellValue());
        }
        lists.add(list);
    }

    doSomeThing(lists);
}

環境情報 (pom.xml抜粋)

<!-- https://mvnrepository.com/artifact/org.apache.poi/poi -->
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>5.0.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml -->
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>5.0.0</version>
</dependency>
1
0
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
1
0