使用 EasyExcel 相邻数据相同时行和列的合并,包括动态表头、数据

前言

在处理 Excel 文件时,经常会遇到需要对表格中的某些单元格进行合并的情况,例如合并相同的行或列。Apache POI 是一个强大的工具,但它使用起来相对复杂。相比之下,EasyExcel 是一个基于 Apache POI 的轻量级 Excel 处理库,它提供了更简单易用的 API,使得处理 Excel 文件变得更加方便。

本文将介绍如何使用 EasyExcel 进行列和列的合并,并提供一个完整的示例代码。

准备工作

首先,确保你的项目中已经引入了 EasyExcel 的依赖。如果你使用的是 Maven,可以在 pom.xml 中添加以下依赖:

<dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>2.2.10</version>
</dependency>
创建合并策略

EasyExcel 提供了一个 AbstractMergeStrategy 抽象类,我们可以继承它来实现自定义的合并策略。下面是一个示例,展示了如何创建一个可以同时进行行和列合并的策略:

package org.songtang.exceldemo.test;import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.write.merge.AbstractMergeStrategy;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;import java.util.*;public class OptimizedMergeCellStrategyHandler extends AbstractMergeStrategy {private final boolean alikeColumn;private final boolean alikeRow;private final int rowIndex;private final int rowIndexStart;private final Set<Integer> columns;private int currentRowIndex = 0;public OptimizedMergeCellStrategyHandler(boolean alikeColumn, boolean alikeRow, int rowIndex, Set<Integer> columns) {this(alikeColumn, alikeRow, rowIndex, columns, 0);}public OptimizedMergeCellStrategyHandler(boolean alikeColumn, boolean alikeRow, int rowIndex, Set<Integer> columns, int rowIndexStart) {this.alikeColumn = alikeColumn;this.alikeRow = alikeRow;this.rowIndex = rowIndex;this.columns = columns;this.rowIndexStart = rowIndexStart;}@Overrideprotected void merge(Sheet sheet, Cell cell, Head head, Integer integer) {int rowId = cell.getRowIndex();currentRowIndex = rowId;if (rowIndex > rowId) {return;}int columnId = cell.getColumnIndex();if (alikeColumn && columnId > 0) {mergeCells(sheet, cell, columnId - 1, columnId, 0);}if (alikeRow && rowId > rowIndexStart && columns.contains(columnId)) {mergeCells(sheet, cell, rowId - 1, rowId, 1);}}private void mergeCells(Sheet sheet, Cell cell, int start, int end, int direction) {String cellValue = getCellVal(cell);Cell referenceCell = direction == 0 ? cell.getRow().getCell(start) : sheet.getRow(start).getCell(cell.getColumnIndex());String refCellValue = getCellVal(referenceCell);if (Objects.equals(cellValue, refCellValue)) {CellRangeAddress rangeAddress = createRangeAddress(sheet, cell, start, end, direction);if (rangeAddress != null) {sheet.addMergedRegion(rangeAddress);}}}private CellRangeAddress createRangeAddress(Sheet sheet, Cell cell, int start, int end, int direction) {CellRangeAddress rangeAddress = direction == 0 ?new CellRangeAddress(cell.getRowIndex(), cell.getRowIndex(), start, end) :new CellRangeAddress(start, end, cell.getColumnIndex(), cell.getColumnIndex());return findExistAddress(sheet, rangeAddress, getCellVal(cell));}private CellRangeAddress findExistAddress(Sheet sheet, CellRangeAddress rangeAddress, String currentVal) {List<CellRangeAddress> mergedRegions = sheet.getMergedRegions();for (int i = mergedRegions.size() - 1; i >= 0; i--) {CellRangeAddress exist = mergedRegions.get(i);if (exist.intersects(rangeAddress)) {if (exist.getLastRow() < rangeAddress.getLastRow()) {exist.setLastRow(rangeAddress.getLastRow());}if (exist.getLastColumn() < rangeAddress.getLastColumn()) {exist.setLastColumn(rangeAddress.getLastColumn());}sheet.removeMergedRegion(i);return exist;}}return rangeAddress;}private String getCellVal(Cell cell) {try {return cell.getStringCellValue();} catch (Exception e) {// 使用日志框架代替 System.out.printf// Logger logger = LoggerFactory.getLogger(OptimizedMergeCellStrategyHandler.class);// logger.error("读取单元格内容失败:行{} 列{}", cell.getRowIndex() + 1, cell.getColumnIndex() + 1, e);System.out.printf("读取单元格内容失败:行%d 列%d %n", (cell.getRowIndex() + 1), (cell.getColumnIndex() + 1));return null;}}
}
编写测试代码

接下来,我们编写一个测试类来生成一个包含合并行和列的 Excel 文件:

package org.songtang.exceldemo;import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import org.junit.jupiter.api.Test;
import org.songtang.exceldemo.test.OptimizedMergeCellStrategyHandler;import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;//@SpringBootTest
public class ExcelTest {@Testpublic void generateRowMergedFile() {String fileName = "/Users/test/Downloads/" + System.currentTimeMillis() + ".xlsx";ExcelWriterBuilder write = EasyExcel.write(fileName);Set<Integer> set = new HashSet<>();set.add(0); // 合并第0列set.add(1); // 合并第1列set.add(2); // 合并第2列write.registerWriteHandler(new OptimizedMergeCellStrategyHandler(true, true, 2, set)); // 启用列和行合并write.head(head()).automaticMergeHead(true).sheet("模板").doWrite(data1());}private List<List<String>> data1() {List<List<String>> data = new ArrayList<>();List<String> data1 = new ArrayList<>();data1.add("人员");data1.add("人员");data1.add("语文");data1.add("数值一");data1.add("数值二");List<String> data2 = new ArrayList<>();data2.add("人员");data2.add("人员");data2.add("语文");data2.add("数值三");data2.add("数值四");data.add(data1);data.add(data2);return data;}private List<List<String>> head() {List<List<String>> list = new ArrayList<>();List<String> head0 = new ArrayList<>();head0.add("模块");head0.add("模块");List<String> head00 = new ArrayList<>();head00.add("模块");head00.add("模块");List<String> head1 = new ArrayList<>();head1.add("课程");head1.add("课程");List<String> head2 = new ArrayList<>();head2.add("完美世界");head2.add("石昊");List<String> head3 = new ArrayList<>();head3.add("完美世界");head3.add("火灵儿");list.add(head0);list.add(head00);list.add(head1);list.add(head2);list.add(head3);return list;}
}
运行测试

运行 generateRowMergedFile 测试方法,将会在指定路径生成一个包含合并行和列的 Excel 文件。你可以打开生成的文件,查看合并的效果。

总结

通过上述步骤,我们成功地使用 EasyExcel 实现了 Excel 文件中行和列的合并。EasyExcel 的强大之处在于其简洁的 API 和灵活的扩展能力,使得复杂的 Excel 处理任务变得简单易行。希望本文对你有所帮助!

如果你有任何问题或建议,欢迎留言交流!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/452847.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【QT】常用控件(三)

个人主页~ 常用控件&#xff08;一&#xff09;~ 常用控件&#xff08;二&#xff09;~ QT中其他线程是改变不了GUI上的内容的&#xff0c;只有主线程可以 常用控件 四、显示类控件2、LCD Number3、ProgressBar4、Calendar Widget 五、输入类控件1、Line Edit正则表达式 2、Te…

【数据处理】大数据入门

&#x1f308; 个人主页&#xff1a;十二月的猫-CSDN博客 &#x1f525; 系列专栏&#xff1a; &#x1f3c0;软件开发必备知识_十二月的猫的博客-CSDN博客 &#x1f4aa;&#x1f3fb; 十二月的寒冬阻挡不了春天的脚步&#xff0c;十二点的黑夜遮蔽不住黎明的曙光 目录 1. 前…

UE小:UE5的Pixelstreaming在捕获画面的时候没办法显示非Viewport的Slate区域按钮的ToolTip

原始代码 首先&#xff0c;让我们看看原始代码片段&#xff1a; // Some widgets might want to provide an alternative Tooltip Handler. if (bCanSpawnNewTooltip || !NewTooltip) {TSharedPtr<SWidget> NewTooltipWidget NewTooltip ? NewTooltip->AsWidget()…

[含文档+PPT+源码等]精品基于springboot实现的原生微信小程序小区兼职系统

基于Spring Boot实现的原生微信小程序小区兼职系统背景&#xff0c;可以从以下几个方面进行阐述&#xff1a; 一、技术背景 移动互联网的普及&#xff1a;随着移动互联网的快速发展&#xff0c;微信小程序作为一种轻量级应用&#xff0c;因其无需下载安装、即用即走的特点&am…

【Next.js 项目实战系列】02-创建 Issue

原文链接 CSDN 的排版/样式可能有问题&#xff0c;去我的博客查看原文系列吧&#xff0c;觉得有用的话&#xff0c;给我的库点个star&#xff0c;关注一下吧 上一篇【Next.js 项目实战系列】01-创建项目 创建 Issue 配置 MySQL 与 Prisma​ 在数据库中可以找到相关内容&…

Greenhills学习总结

学习背景&#xff1a;近期参与xx项目过程中&#xff0c;遇到较多的关于代码集成编译的知识盲区&#xff0c;因此需要进行相关知识的学习和扫盲。 参考资料&#xff1a;GreenHills2017.7编译手册:本手册是GreenHills 2017.7.14版编译器的软件使用手册。该手册详细介绍了GreenHi…

数学中的直觉、联想和抽象漫谈

数学中的直觉、联想和抽象漫谈 直觉、联想和抽象不是孤立存在的&#xff0c;而是相互交织、共同作用的。构成了我们认知理解世界的不可或缺的三种能力。我们应该重视并培养这些思维能力&#xff0c;以更好地适应不断变化的世界。 在数学的世界里&#xff0c;直觉、联想和抽象是…

【每日一题】24.10.14 - 24.10.20

10.14 直角三角形1. 题目2. 解题思路3. 代码实现&#xff08;AC_Code&#xff09; 10.15 回文判定1. 题目2. 解题思路3. 代码实现&#xff08;AC_Code&#xff09; 10.16 二次方程1. 题目2. 解题思路3. 代码实现&#xff08;AC_Code&#xff09; 10.17 互质1. 题目2. 解题思路3…

UE5 gameplay学习1 蓝图修改材质和参数

第一种是直接修改这个材质&#xff0c;比较朴素 这个对象直接Set Material这个很直观就设置了 如果要设置材质的属性&#xff0c;就有一点奇怪了&#xff0c;通常来说get到material就能设置了&#xff0c;这里需要如下操作 create一个dynamic material instance 然后还要指定…

[JAVAEE] 线程安全问题

目录 一. 什么是线程安全 二. 线程安全问题产生的原因 三. 线程安全问题的解决 3.1 解决修改操作不是原子性的问题 > 加锁 a. 什么是锁 b. 没有加锁时 c. 加锁时 d. 死锁 e. 避免死锁 3.2 解决内存可见性的问题 > volatile关键字 (易变的, 善变的) a. 不加…

搭建Golang gRPC环境:protoc、protoc-gen-go 和 protoc-gen-go-grpc 工具安装教程

参考文章&#xff1a; 安装protoc、protoc-gen-go、protoc-gen-go-grpc-CSDN博客 一、简单介绍 本文开发环境&#xff0c;均为 windows 环境&#xff0c;mac 环境其实也类似 ~ ① 编译proto文件&#xff0c;相关插件 简单介绍&#xff1a; protoc 是编译器&#xff0c;用于将…

AUTOSAR_EXP_ARAComAPI的5章笔记(17)

☞返回总目录 相关总结&#xff1a;AutoSar AP CM通信组总结 5.7 通信组 5.7.1 目标 通信组&#xff08;Communication Group&#xff0c;CG&#xff09;是由 AUTOSAR 定义的复合服务模板。它提供了一个通信框架&#xff0c;允许在 AUTOSAR 应用程序之间以对等方式和广播模…

AMBA-CHI协议详解(十)

AMBA-CHI协议详解&#xff08;一&#xff09;- Introduction AMBA-CHI协议详解&#xff08;二&#xff09;- Channel fields / Read transactions AMBA-CHI协议详解&#xff08;三&#xff09;- Write transactions AMBA-CHI协议详解&#xff08;四&#xff09;- Other transac…

【设计模式系列】抽象工厂模式

一、什么是抽象工厂模式 抽象工厂模式&#xff08;Abstract Factory Pattern&#xff09;是一种创建型设计模式&#xff0c;它提供了一个接口&#xff0c;用于创建一系列相关或相互依赖的对象&#xff0c;而无需指定它们具体的类。这种模式允许客户端使用抽象的接口来创建一组…

一小时快速入门Android GPU Inspector

本文介绍如何使用 Android GPU Inspector (AGI) 对Android 应用进行系统性能分析和帧性能分析 。面向熟悉Android图形的开发者。 待分析应用需要的前置条件 (1) 将应用设置为可调试状态 <application [...] android:debuggable"true">&#xff08;2&#xff09…

LabVIEW水质监测系统

在面对全球性的海洋污染问题时&#xff0c;利用先进技术进行水质监测成为了保护海洋环境的关键手段之一。开发了一种基于LabVIEW的海洋浮标水质监测系统&#xff0c;该系统能够实时监测并评估近海水域的水质状况&#xff0c;旨在为海洋保护和污染防治提供科技支持。 项目背景 …

svn-拉取与更新代码

右键项目文件 进行更新与提交代码&#xff0c;提交代码选择更改的文件以及填写commit

电子部授课1

今天下午有院科协的授课&#xff0c;涉及电赛知识&#xff0c;单片机环境构建和模拟方向讲解。感觉要学知识还是很多呜呜呜 这是电赛讲解&#xff0c;主要是五个方面&#xff0c;有一个讲太快了没有听清哈哈哈 后面是全程搜概念的模拟&#xff0c;真的有很多知识不太明白 慌乱…

Java项目-基于springboot框架的会员制医疗预约服务管理信息系统项目实战(附源码+文档)

作者&#xff1a;计算机学长阿伟 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、ElementUI等&#xff0c;“文末源码”。 开发运行环境 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringBoot、Vue、Mybaits Plus、ELementUI工具&#xff1a;IDEA/…

云曦10月13日awd复现

一、防御 1、改用户密码 passwd <user> 2、改数据库密码 进入数据库 mysql -uroot -proot 改密码 update mysql.user set passwordpassword(新密码) where userroot; 查看用户信息密码 select host,user,password from mysql.user; 改配置文件&#xff0c;将密码改为自己…