【Java】POI 解析有合并单元格的 Excel

需求

将带有“合并单元格”的 Excel 进行解析,Excel 示例如下:

方案

Java 生态中 Excel 解析库比较多,下面介绍两种比较常用的库

  1. Apache POI:POI 是用于读写 Microsoft Office 格式文件(如 Excel、Word 和 PowerPoint)的 Java 库。它提供了一组丰富的功能和 API,可用于读取和写入 Excel 文件的各种格式,如 XLS、XLSX 和 CSV 等。
  2. EasyExcel是阿里巴巴开源的一个基于 Java 的简单、省内存的读写 Excel 的开源项目。EasyExcel 在尽可能节约内存的情况下支持读写百 M 级的 Excel 。EasyExcel 能大大减少占用内存的主要原因是在解析Excel时没有将文件数据一次性全部加载到内存中,而是从磁盘上一行行读取数据,逐个解析。EasyExcel 采用一行一行的解析模式,并将一行的解析结果以观察者的模式通知处理(AnalysisEventListener)。

在本示例中,将使用 POI 来解析 Excel 。需要注意的是 ,POI 对 xlsxlsx 格式的 Excel 采用不同的 API ,分别是 org.apache.poi.hssf.usermodel.HSSFWorkbookorg.apache.poi.xssf.usermodel.XSSFWorkbook

依赖引入

        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>4.1.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>4.1.0</version>
        </dependency>

代码示例

以下示例展示了解析 xls 格式的 Excel 。

package com.xzbd.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.List;
import java.util.Objects;

import org.apache.commons.lang.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddress;

import cn.hutool.core.collection.CollectionUtil;

public class ExcelUtil {
    public static void main(String[] args) throws IOException {

        String filePath = "<your-excel-dir>\\ParseDemo.xls";
        File file = new File(filePath);
        if (!file.exists()) {
            System.out.println(" File not exists ...");
            return;
        }

        FileInputStream fis = new FileInputStream(file);
        Workbook workbook = new HSSFWorkbook(fis);
        Sheet sheet = workbook.getSheetAt(0);
        List<CellRangeAddress> mergedRegions = sheet.getMergedRegions();
        // 遍历每行数据
        for (Row row : sheet) {
            // 遍历每个单元格
            for (Cell cell : row) {
                CellRangeAddress cellRangeAddress = getCellRangeAddress(mergedRegions, cell);
                String value = "";
                if (Objects.nonNull(cellRangeAddress)) {
                    value = getCellRangeAddressValue(cellRangeAddress, sheet);
                } else {
                    value = getStringValue(cell);
                }
                System.out.print(value + "\t");
            }
            System.out.println();
        }
        // 关闭资源
        workbook.close();
        fis.close();
    }

    public static String getCellRangeAddressValue(CellRangeAddress cellRangeAddress, Sheet sheet) {
        if (Objects.isNull(cellRangeAddress)) {
            return StringUtils.EMPTY;
        }
        if (Objects.isNull(sheet)) {
            return StringUtils.EMPTY;
        }
        Row row = sheet.getRow(cellRangeAddress.getFirstRow());
        if (Objects.isNull(row)) {
            return StringUtils.EMPTY;
        }
        Cell cell = row.getCell(cellRangeAddress.getFirstColumn());
        if (Objects.isNull(cell)) {
            return StringUtils.EMPTY;
        }
        return getStringValue(cell);
    }

    public static CellRangeAddress getCellRangeAddress(List<CellRangeAddress> mergedRegions, Cell cell) {
        if (CollectionUtil.isEmpty(mergedRegions)) {
            return null;
        }
        if (Objects.isNull(cell)) {
            return null;
        }

        for (CellRangeAddress mergedRegion : mergedRegions) {

            Boolean in = mergedRegion.isInRange(cell);
            if (in) {
                return mergedRegion;
            }
        }

        return null;
    }

    /**
     * 判断当前单元格是否在合并单元格区域内
     * 
     * @param mergedRegion
     * @param cell
     * @return
     */
    public static Boolean isInMergedRegin(CellRangeAddress mergedRegion, Cell cell) {
        if (Objects.isNull(mergedRegion)) {
            return false;
        }
        if (Objects.isNull(cell)) {
            return false;
        }
        int row = cell.getRowIndex();
        int column = cell.getColumnIndex();
        int firstColumn = mergedRegion.getFirstColumn();
        int lastColumn = mergedRegion.getLastColumn();
        int firstRow = mergedRegion.getFirstRow();
        int lastRow = mergedRegion.getLastRow();
        // 判断当前单元格是否在合并单元格区域内
        if ((row >= firstRow && row <= lastRow) && (column >= firstColumn && column <= lastColumn)) {
            return true;
        }

        return false;
    }

    public static String getStringValue(Cell cell) {
        String value = "";
        // 判断单元格类型
        if (cell.getCellType() == CellType.STRING) {
            value = cell.getStringCellValue();
        } else if (cell.getCellType() == CellType.BOOLEAN) {
            value = String.valueOf(cell.getBooleanCellValue());
        } else if (cell.getCellType() == CellType.NUMERIC) {
            value = String.valueOf(cell.getNumericCellValue());
        }
        return value;
    }
}


运行结果

以下是解析 Excel ,打印出的结果

xxx相关信息     xxx相关信息     xxx相关信息
大类    小类    材料
水果类  苹果    花牛苹果
水果类  香蕉    老山香蕉
水果类  梨      巴山雪梨
蔬菜类  蒜苗    红皮蒜苗
蔬菜类  蒜苗    白皮蒜苗
蔬菜类  辣椒    小米椒
蔬菜类  茄子    胖茄子
肉类    鱼类    三文鱼
肉类    鱼类    金枪鱼
肉类    鱼类    耗儿鱼

总结

本文基于 xls 格式的 Excel 给出了解析合并单元格的示例。且经过运行打印出了解析结果。

要将工具类改为支持 xlsx 格式的 Excel 解析,只需要如下两步操作:

  • HSSFWorkbook 改为 XSSFWorkbook 并导入包 org.apache.poi.xssf.usermodel.XSSFWorkbook
  • String filePath = "<your-excel-dir>\\ParseDemo.xls"; 改为 String filePath = "<your-excel-dir>\\ParseDemo.xlsx"; 注意 ParseDemo.xlsx 不是仅仅更改名称,还要创建正确的文件。
  • 12
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
解析合并单元格数据,可以使用 Apache POI 库来读取 Excel 文件中的单元格数据。下面是一个简单的示例代码: ```java FileInputStream file = new FileInputStream(new File("example.xlsx")); XSSFWorkbook workbook = new XSSFWorkbook(file); XSSFSheet sheet = workbook.getSheetAt(0); // 遍历每一行 for (Row row : sheet) { // 遍历每个单元格 for (Cell cell : row) { // 获取单元格的地址 CellAddress address = cell.getAddress(); // 获取单元格的值 String value = cell.getStringCellValue(); // 获取单元格合并信息 CellRangeAddress mergedRegion = getMergedRegion(sheet, address.getRow(), address.getColumn()); // 如果单元格合并单元格获取合并后的值 if (mergedRegion != null) { int firstRow = mergedRegion.getFirstRow(); int firstCol = mergedRegion.getFirstColumn(); Cell mergedCell = sheet.getRow(firstRow).getCell(firstCol); value = mergedCell.getStringCellValue(); } System.out.println("Address: " + address + ", Value: " + value); } } // 获取单元格合并信息 private static CellRangeAddress getMergedRegion(XSSFSheet sheet, int row, int column) { for (int i = 0; i < sheet.getNumMergedRegions(); i++) { CellRangeAddress mergedRegion = sheet.getMergedRegion(i); if (mergedRegion.isInRange(row, column)) { return mergedRegion; } } return null; } ``` 这个示例代码遍历了 Excel 文件中的每一个单元格,并且通过 `getMergedRegion` 方法判断单元格是否属于一个合并单元格,如果是,则获取合并单元格的值。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值