Apache POI合并单元格解惑

Apache POI,是一款用来读写Excel文档的神器。早期,Apache POI只有用Java语言开发的版本,现在,.NET社区已经翻译出C#版本的NPOI

虽然POI强大、好用、而且还是免费的,但仍有一些奇怪、费解的地方,比如当合并单元格(POI 3.8)的时候,出现了如下这样奇葩的结果:

POI

合并单元格是成功,还是失败?POI 3.8没有任何告警、提示,只看上面图中的合并后的效果,[A4:B5]区域虽然中间的框线已经合并没了,但还是能够看出是分成上下2块的,有点摸不着头绪。在升级到POI 3.17之后,偶然发现多了个addMergedRegionUnsafe方法,此时继续调用addMergedRegion方法导出上面的Excel文件,就会导出失败,控制台打印出如下错误:

java.lang.IllegalStateException: Cannot add merged region A4:B5 to sheet because it overlaps with an existing merged region (A5:B5).

意思是说,需要合并的区域[A4:B5]与已合并好的区域[A5:B5]冲突了。

知道具体的bug成因,解决起来就easy了。解决方案是,如果需要合并的目标区域内已经有合并好的单元格了,就先把这些合并好的单元格拆掉,再继续合并,完整的代码如下:

/**
 * 合并单元格;
 *
 * @param sheet       工作簿;
 * @param firstRow    起始行;
 * @param lastRow     结束行;
 * @param firstColumn 起始列;
 * @param lastColumn  结束列;
 */
public static void mergeCells(HSSFSheet sheet, int firstRow, int lastRow, int firstColumn, int lastColumn) {
    CellRangeAddress mergedRegion;
    for (int i = sheet.getNumMergedRegions() - 1; i >= 0; i--) {
        mergedRegion = sheet.getMergedRegion(i);
        if (!(firstRow > mergedRegion.getLastRow() || lastRow < mergedRegion.getFirstRow() || firstColumn > mergedRegion.getLastColumn() || lastColumn < mergedRegion.getFirstColumn())) {
            sheet.removeMergedRegion(i);
        }
    }
    CellRangeAddress region = new CellRangeAddress(firstRow, lastRow, firstColumn, lastColumn);
    sheet.addMergedRegion(region);
}
写于 2019年12月30日