使用 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 处理任务变得简单易行。希望本文对你有所帮助!
如果你有任何问题或建议,欢迎留言交流!
相关文章:
使用 EasyExcel 相邻数据相同时行和列的合并,包括动态表头、数据
前言 在处理 Excel 文件时,经常会遇到需要对表格中的某些单元格进行合并的情况,例如合并相同的行或列。Apache POI 是一个强大的工具,但它使用起来相对复杂。相比之下,EasyExcel 是一个基于 Apache POI 的轻量级 Excel 处理库&am…...
985研一学习日记 - 2024.10.16
一个人内耗,说明他活在过去;一个人焦虑,说明他活在未来。只有当一个人平静时,他才活在现在。 日常 1、起床6:00√ 2、健身1个多小时 今天练了二头和背部,明天练胸和三头 3、LeetCode刷了3题 旋转图像:…...
安装mysql 5.5.62
1>先检查是否存在其他版本mysql rpm -qa|grep -i mariadb 存在则卸载 yum -y remove maria* 2>下载mysql 5.5.62 wget https://cdn.mysql.com/archives/mysql-5.5/mysql-5.5.62-linux-glibc2.12-x86_64.tar.gz 3>确认系统是否安装libaio库 yum -y install libai…...

AnaTraf | 网络性能监控系统的价值
目录 1. IT运维工程师 2. 网络管理员 3. 安全团队(网络安全工程师) 4. 业务部门(应用开发人员、产品经理) 5. 管理层与决策者(CTO/CIO、IT经理) 6. 最终用户(普通员工) 总结&…...

决策树和集成学习的概念以及部分推导
一、决策树 1、概述 决策树是一种树形结构,树中每个内部节点表示一个特征上的判断,每个分支代表一个判断结果的输出,每个叶子节点代表一种分类结果 决策树的建立过程: 特征选择:选择有较强分类能力的特征决策树生成…...

servlet基础与环境搭建(idea版)
文章目录 环境变量配置安包装环境变量配置JDK 配置 静态网页动态网页(idea)给模块添加 web框架新版本 2023 之后的 idea,使用方法二idea 目录介绍建立前端代码启动配置 环境变量配置 tomcat 环境变量 安包装 环境变量配置 JDK 配置 静态网页…...

【10月最新】植物大战僵尸杂交版新僵尸预告(附最新版本下载链接)
【BOSS僵尸】埃德加二世 【新BOSS僵尸】埃德加二世 “埃德加博士的克隆体。驾驶着最新一代小型化机甲,致力于为戴夫博士扫清障碍。” -体型(模型大小)小于原版僵王的头 -血量120000(原版僵王复仇的2倍),免疫…...
网络编程-UDP以及数据库mysql
UDP通信流程 服务端客户端有一个邮箱socket()有一个邮箱socket()绑定地址bind()发送数据sendto接收数据recvfrom关闭close()关闭colse() //服务端 #include "head.h" // ./server 10001 int main(int argc,char *argv[]) {// 1、创建socket套接字// 参数1ÿ…...

ubuntu 20.04 安装ros1
步骤 1:设置系统 首先,确保系统环境是最新的: sudo apt update sudo apt upgrade 步骤 2:设置源和密钥 添加 ROS 软件源: 首先,确保 curl 和 gnupg 已安装: sudo apt install curl gnupg2…...

ShardingSphere-Proxy 数据库中间件MySql分库分表环境搭建
一. ShardingSphere-Proxy简介 1、简介 Apache ShardingSphere 是一款开源分布式数据库生态项目,旨在碎片化的异构数据库上层构建生态,在最大限度的复用数据库原生存算能力的前提下,进一步提供面向全局的扩展和叠加计算能力。其核心采用可插…...

Pytest+selenium UI自动化测试实战实例
🍅 点击文末小卡片,免费获取软件测试全套资料,资料在手,涨薪更快 今天来说说pytest吧,经过几周的时间学习,有收获也有疑惑,总之最后还是搞个小项目出来证明自己的努力不没有白费 环境准备 1 …...

服务器技术研究分析:存储从HBM到CXL
服务器变革:存储从HBM到CXL 在《从云到端,AI产业的新范式(2024)》中揭示,传统服务器价格低至1万美金,而配备8张H100算力卡的DGX H100AI服务器价值高达40万美金(约300万人民币)。 从供…...
下载并安装 WordPress 中文版
下载并安装 WordPress 中文版 1. 安装 LAMP 环境(Linux, Apache, MySQL, PHP)1. 安装 Apache2. 安装 MySQL3. 安装 PHP1. 下载并安装 WordPress 中文版1. 下载 WordPress2. 配置文件权限3 . 创建 MySQL 数据库4 . 配置 WordPress1. 安装 LAMP 环境(Linux, Apache, MySQL, PH…...
从零开始的LeetCode刷题日记:515.在每个树行中找最大值
一.相关链接 题目链接:515.在每个树行中找最大值 二.心得体会 这道题也是层序遍历,只需要记录每一层的最大值即可,反复比较记录最大值。 三.代码 class Solution { public:vector<int> largestValues(TreeNode* root) {vector<…...

C语言 | Leetcode C语言题解之第492题构造矩形
题目: 题解: class Solution { public:vector<int> constructRectangle(int area) {int w sqrt(1.0 * area);while (area % w) {--w;}return {area / w, w};} };...
在FastAPI网站学python:虚拟环境创建和使用
Python虚拟环境(virtual environment)是一个非常重要的工具,它允许开发者为每个项目创建独立的Python环境,隔离您为每个项目安装的软件包,从而避免不同项目之间的依赖冲突。 学习参考FastAPI官网文档:Virt…...
安全风险评估(Security Risk Assessment, SRA)
安全风险评估(Security Risk Assessment, SRA)是识别、分析和评价信息安全风险的过程。它帮助组织了解其信息资产面临的潜在威胁,以及这些威胁可能带来的影响。通过风险评估,组织可以制定有效的风险管理策略,以减少或控…...

SQL Injection | SQL 注入 —— 布尔盲注
关注这个漏洞的其他相关笔记:SQL 注入漏洞 - 学习手册-CSDN博客 0x01:布尔盲注 —— 理论篇 布尔盲注(Boolean-Based Blind Injection)是一种常见的 SQL 注入技术,它适用于那些 SQL 注入时,查询结果不会直…...

stm32 bootloader写法
bootloader写法: 假设app的起始地址:0x08020000,则bootloader的范围是0x0800,0000~0x0801,FFFF。 #define APP_ADDR 0x08020000 // 应用程序首地址定义 typedef void (*APP_FUNC)(void); // 函数指针类型定义 /*main函数中调用rum_app&#x…...
Unity3D 物体表面水滴效果详解
在游戏开发中,逼真的水滴效果能够显著提升游戏场景的真实感和沉浸感。Unity3D作为一款强大的游戏开发引擎,提供了丰富的工具和技术来实现这种效果。本文将详细介绍如何在Unity3D中实现物体表面的水滴效果,包括技术详解和代码实现。 对惹&…...
Java 语言特性(面试系列1)
一、面向对象编程 1. 封装(Encapsulation) 定义:将数据(属性)和操作数据的方法绑定在一起,通过访问控制符(private、protected、public)隐藏内部实现细节。示例: public …...
基于服务器使用 apt 安装、配置 Nginx
🧾 一、查看可安装的 Nginx 版本 首先,你可以运行以下命令查看可用版本: apt-cache madison nginx-core输出示例: nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...

深入理解JavaScript设计模式之单例模式
目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式(Singleton Pattern&#…...
Swagger和OpenApi的前世今生
Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章,二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑: 🔄 一、起源与初创期:Swagger的诞生(2010-2014) 核心…...
在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?
uni-app 中 Web-view 与 Vue 页面的通讯机制详解 一、Web-view 简介 Web-view 是 uni-app 提供的一个重要组件,用于在原生应用中加载 HTML 页面: 支持加载本地 HTML 文件支持加载远程 HTML 页面实现 Web 与原生的双向通讯可用于嵌入第三方网页或 H5 应…...

CVPR2025重磅突破:AnomalyAny框架实现单样本生成逼真异常数据,破解视觉检测瓶颈!
本文介绍了一种名为AnomalyAny的创新框架,该方法利用Stable Diffusion的强大生成能力,仅需单个正常样本和文本描述,即可生成逼真且多样化的异常样本,有效解决了视觉异常检测中异常样本稀缺的难题,为工业质检、医疗影像…...

【Linux手册】探秘系统世界:从用户交互到硬件底层的全链路工作之旅
目录 前言 操作系统与驱动程序 是什么,为什么 怎么做 system call 用户操作接口 总结 前言 日常生活中,我们在使用电子设备时,我们所输入执行的每一条指令最终大多都会作用到硬件上,比如下载一款软件最终会下载到硬盘上&am…...
6个月Python学习计划 Day 16 - 面向对象编程(OOP)基础
第三周 Day 3 🎯 今日目标 理解类(class)和对象(object)的关系学会定义类的属性、方法和构造函数(init)掌握对象的创建与使用初识封装、继承和多态的基本概念(预告) &a…...

PydanticAI快速入门示例
参考链接:https://ai.pydantic.dev/#why-use-pydanticai 示例代码 from pydantic_ai import Agent from pydantic_ai.models.openai import OpenAIModel from pydantic_ai.providers.openai import OpenAIProvider# 配置使用阿里云通义千问模型 model OpenAIMode…...

PLC入门【4】基本指令2(SET RST)
04 基本指令2 PLC编程第四课基本指令(2) 1、运用上接课所学的基本指令完成个简单的实例编程。 2、学习SET--置位指令 3、RST--复位指令 打开软件(FX-TRN-BEG-C),从 文件 - 主画面,“B: 让我们学习基本的”- “B-3.控制优先程序”。 点击“梯形图编辑”…...