JAVA解析Excel复杂表头
废话不多说,直接上源码。前后端都有哦~~~~~~~~
能帮到你记得点赞收藏哦~~~~~~~~~
后端:
import org.apache.poi.ss.usermodel.Cell;
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 org.apache.poi.xssf.usermodel.XSSFWorkbook;import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;/*** @Description:Excel工具类(复杂表头解析)* @Author: sh* @Date: 2025/1/2 09:29*/
public class ExcelComplexUtil {/*** 导入Excel文件,逐行读取数据,数据格式二维数组* @param filePath* @param sheetIndex* @return* @throws IOException*/public String[][] importExcel(String filePath, int sheetIndex) throws IOException {List<String[]> dataList = new ArrayList<>();try (FileInputStream fis = new FileInputStream(new File(filePath));Workbook workbook = new XSSFWorkbook(fis)) {Sheet sheet = workbook.getSheetAt(sheetIndex); // 获取第一个工作表// 获取表头行Row headerRow = checkHeaderRow(sheet);if (headerRow != null) {//封装表头数据warpHeaderData(headerRow, dataList);} else {throw new RuntimeException("Excel 文件中没有找到表头行,请修改表格");}// 读取数据行for (int rowIndex = 1; rowIndex <= sheet.getLastRowNum(); rowIndex++) {Row row = sheet.getRow(rowIndex);if (row == null) {continue; // 跳过空行}//封装行数据warpRowData(headerRow, row, dataList);}}// 将 List<String[]> 转换为 String[][] 数组String[][] result = new String[dataList.size()][];for (int i = 0; i < dataList.size(); i++) {result[i] = dataList.get(i);}return result; // 返回二维数组}/*** 检查表头行,表头行必须在前10行中* @param sheet* @return*/private Row checkHeaderRow(Sheet sheet) {int i = 0;Row headerRow = null;while (i < 10) {headerRow = sheet.getRow(i);if (headerRow != null) {break;}i++;}return headerRow;}/*** 数据遍历* @param headerRow* @param dataList* @throws IOException*/public void warpHeaderData(Row headerRow, List<String[]> dataList) {int columnCount = headerRow.getPhysicalNumberOfCells();short lastCellNum = headerRow.getLastCellNum();String[] data = new String[columnCount]; // 创建一维数组存储表头数据for (int colIndex = 0; colIndex < columnCount; colIndex++) {Cell cell = headerRow.getCell(lastCellNum - columnCount + colIndex);String cellValue = (cell != null) ? cell.toString() : ""; // 处理空单元格data[colIndex] = cellValue; // 将单元格值放入表头数组中}dataList.add(data); // 将表头数组添加到列表中}public void warpRowData(Row headerRow, Row row, List<String[]> dataList) {int columnCount = headerRow.getPhysicalNumberOfCells();short lastCellNum = headerRow.getLastCellNum();String[] data = new String[columnCount]; // 创建一维数组存储表头数据for (int colIndex = 0; colIndex < columnCount; colIndex++) {Cell cell = row.getCell(lastCellNum - columnCount + colIndex);String cellValue = (cell != null) ? cell.toString() : ""; // 处理空单元格data[colIndex] = cellValue; // 将单元格值放入表头数组中}dataList.add(data); // 将表头数组添加到列表中}/*** 获取excel中所有合并单元格* @param filePath* @param sheetIndex* @throws IOException*/public List<MergedCell> checkMergedCells(String filePath, int sheetIndex) throws IOException {try (FileInputStream fis = new FileInputStream(new File(filePath));Workbook workbook = new XSSFWorkbook(fis)) {Sheet sheet = workbook.getSheetAt(sheetIndex); // 获取第一个工作表int numberOfMergedRegions = sheet.getNumMergedRegions(); // 获取合并单元格的数量List<MergedCell> mergedCellArray = new ArrayList<>();for (int i = 0; i < numberOfMergedRegions; i++) {MergedCell mergedCell = new MergedCell();CellRangeAddress range = sheet.getMergedRegion(i); // 获取合并单元格区域mergedCell.setRange(range.formatAsString());// 获取合并单元格区域的起始单元格int firstRow = range.getFirstRow();int firstCol = range.getFirstColumn();// 获取合并单元格的内容Row row = sheet.getRow(firstRow);Cell cell = row.getCell(firstCol);String cellValue = (cell != null) ? cell.toString() : ""; // 处理空单元格mergedCell.setValue(cellValue);mergedCellArray.add(mergedCell);}return mergedCellArray;}}/*** 检查特定单元格是否是合并单元格* @param sheet* @param row* @param col* @return*/private boolean isMergedCell(Sheet sheet, int row, int col) {int numberOfMergedRegions = sheet.getNumMergedRegions();for (int i = 0; i < numberOfMergedRegions; i++) {CellRangeAddress range = sheet.getMergedRegion(i);if (range.isInRange(row, col)) {return true; // 如果该单元格在合并区内,返回 true}}return false; // 如果不在任何合并区内,返回 false}class MergedCell {private String range;private String value;public String getRange() {return range;}public void setRange(String range) {this.range = range;}public String getValue() {return value;}public void setValue(String value) {this.value = value;}@Overridepublic String toString() {return "MergedCell{" +"range='" + range + '\'' +", value='" + value + '\'' +'}';}}// public static void main(String[] args) {
// String filePath = "/ceshi/ceshi.xlsx"; // Excel 文件路径
// try {
// String[][] strings = importExcel(filePath, 0);
// for (String[] row : strings) {
// System.out.println(String.join(", ", row)); // 以逗号为分隔符打印每一行
// }
// List<MergedCell> mergedCells = checkMergedCells(filePath, 0);
// for (MergedCell row : mergedCells) {
// System.out.println(row); // 以逗号为分隔符打印每一行
// }
//
// } catch (IOException e) {
// e.printStackTrace();
// }
// }
}
前端:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>合并单元格查看器</title><style>table {border-collapse: collapse;width: 500px; /* 设置表格宽度 */}td {border: 1px solid black;padding: 0; /* 清除内边距 */text-align: center;vertical-align: middle; /* 垂直居中 */height: 50px; /* 设置每个单元格的高度 */}.merged {background-color: #f0f0f0; /* 合并单元格的背景颜色 */font-weight: bold; /* 合并单元格字体加粗 */}</style>
</head>
<body><div id="app"><h1>合并单元格查看器</h1><table><tr v-for="(row, rowIndex) in tableData" :key="rowIndex"><tdv-for="(cell, colIndex) in row":key="colIndex"v-if="!isCellOccupied(rowIndex, colIndex)":rowspan="getRowSpan(rowIndex, colIndex)":colspan="getColSpan(rowIndex, colIndex)":class="{ merged: isMergedCell(rowIndex, colIndex) }">{{ getCellValue(rowIndex, colIndex) }}</td></tr></table></div><script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script><script>new Vue({el: '#app',data() {return {mergedCells: [{ range: "C1:D1", value: "合并单元格 1" },{ range: "B2:C2", value: "合并单元格 2" },{ range: "B3:C3", value: "合并单元格 " },{ range: "A2:A3", value: "合并单元格 " },{ range: "F2:F3", value: "合并单元格 " } ],normalData: [["监督员地区", "总数", "有效", "", "有效政治类", ""],["平谷区监督员", "244", "", "197", "28", "169"],["", "244", "", "197", "28", ""],["数据 16", "数据 17", "数据 18", "数据 19", "数据 20"],["数据 21", "数据 22", "数据 23", "数据 24", "数据 25"],["", "", "", "", ""]],occupiedCells: []};},computed: {// 生成完整表格数据tableData() {const rows = this.normalData.length;const cols = this.normalData[0].length;const emptyTable = Array.from({ length: rows }, () => Array(cols).fill(null));// 填充合并单元格this.mergedCells.forEach(({ range, value }) => {const [start, end] = range.split(':');const startRow = parseInt(start.match(/\d+/)[0]) - 1;const startCol = start.charCodeAt(0) - 65;const endRow = parseInt(end.match(/\d+/)[0]) - 1;const endCol = end.charCodeAt(0) - 65;// 填充合并单元格的位置for (let r = startRow; r <= endRow; r++) {for (let c = startCol; c <= endCol; c++) {if (r === startRow && c === startCol) {emptyTable[r][c] = value; // 合并单元格的值} else {this.occupiedCells[r] = this.occupiedCells[r] || [];this.occupiedCells[r][c] = true; // 标记占用}}}});// 填充普通单元格的数据this.normalData.forEach((row, r) => {row.forEach((cell, c) => {if (!this.occupiedCells[r] || !this.occupiedCells[r][c]) {emptyTable[r][c] = cell;}});});return emptyTable;}},methods: {isCellOccupied(rowIndex, colIndex) {return this.occupiedCells[rowIndex] && this.occupiedCells[rowIndex][colIndex];},getCellValue(rowIndex, colIndex) {return this.tableData[rowIndex][colIndex];},getRowSpan(rowIndex, colIndex) {let rowspan = 1;const firstValue = this.getCellValue(rowIndex, colIndex);const mergedCell = this.mergedCells.find(({ range }) => {const [start, end] = range.split(':');const startRow = parseInt(start.match(/\d+/)[0]) - 1;const startCol = start.charCodeAt(0) - 65;const endRow = parseInt(end.match(/\d+/)[0]) - 1;const endCol = end.charCodeAt(0) - 65;return rowIndex === startRow && colIndex === startCol;});if (mergedCell) {const [start, end] = mergedCell.range.split(':');const startRow = parseInt(start.match(/\d+/)[0]) - 1;const endRow = parseInt(end.match(/\d+/)[0]) - 1;rowspan = endRow - startRow + 1;}return rowspan;},getColSpan(rowIndex, colIndex) {let colspan = 1;const firstValue = this.getCellValue(rowIndex, colIndex);const mergedCell = this.mergedCells.find(({ range }) => {const [start, end] = range.split(':');const startRow = parseInt(start.match(/\d+/)[0]) - 1;const startCol = start.charCodeAt(0) - 65;const endRow = parseInt(end.match(/\d+/)[0]) - 1;const endCol = end.charCodeAt(0) - 65;return rowIndex === startRow && colIndex === startCol;});if (mergedCell) {const [start, end] = mergedCell.range.split(':');const startCol = start.charCodeAt(0) - 65;const endCol = end.charCodeAt(0) - 65;colspan = endCol - startCol + 1;}return colspan;},isMergedCell(rowIndex, colIndex) {return this.mergedCells.some(({ range }) => {const [start, end] = range.split(':');const startRow = parseInt(start.match(/\d+/)[0]) - 1;const startCol = start.charCodeAt(0) - 65;return rowIndex === startRow && colIndex === startCol;});}}});</script>
</body>
</html>
相关文章:
JAVA解析Excel复杂表头
废话不多说,直接上源码。前后端都有哦~~~~~~~~ 能帮到你记得点赞收藏哦~~~~~~~&#…...
jmeter 中 BeanShell 预处理程序、JSR223后置处理程序使用示例
1. 各个组件如何新建的? 2. "http请求" 组件内容样例: "消息体数据" 源码: {"task_tag": "face_detect","image_type": "base64","extra_args": [{"model"…...
我的创作纪念日——《惊变128天》
我的创作纪念日——《惊变128天》 机缘收获日常成就憧憬 机缘 时光飞逝,转眼间,我已在这条创作之路上走过了 128 天。回顾起 2024 年 8 月 29 日,我满怀忐忑与期待,撰写了第一篇技术博客《讲解LeetCode第1题:两数之和…...
vuedraggable 选项介绍
vuedraggable 是基于 SortableJS 的 Vue 组件,提供了丰富的选项来定制拖拽行为。以下是 vuedraggable 常用的选项和它们的详细说明: 常用选项介绍 group 配置拖拽分组。多个列表可以共享同一个分组,允许它们之间的项目互相拖拽。 group: { na…...
微信小程序获取后端数据
在小程序中获取后端接口数据 通常可以使用 wx.request 方法,以下是一个基本示例: // pages/index/index.js Page({data: {// 用于存储后端返回的数据resultData: [] },onLoad() {this.fetchData();},fetchData() {wx.request({url: https://your-backe…...
ThreadLocal` 的工作原理
ThreadLocal 的工作原理: ThreadLocal 是 Java 提供的一个类,它用于为每个线程提供独立的变量副本。也就是说,多个线程访问同一个 ThreadLocal 变量时,每个线程看到的值都是不同的,相互隔离,互不干扰。 T…...
数据挖掘教学指南:从基础到应用
数据挖掘教学指南:从基础到应用 引言 数据挖掘是大数据时代的核心技术之一,它从大量数据中提取有用信息和知识。本教学文章旨在为学生和初学者提供一个全面的数据挖掘学习指南,涵盖数据挖掘的基本概念、流程、常用技术、工具以及教学建议。…...
大模型搜索引擎增强问答demo-纯python实现
流程概览 本文使用python语言,实现了大模型搜索引擎增强问答demo。 大模型搜索引擎增强问答定义:根据问题搜索得到相关内容,拼接prompt=问题+搜索结果,将这个prompt传入大模型,得到最终的结果。 优势在于搜索引擎可以返回实时性信息,例如明日双色球开奖信息、最新八卦…...
【C语言程序设计——选择结构程序设计】按从小到大排序三个数(头歌实践教学平台习题)【合集】
目录😋 任务描述 编程要求 相关知识 1. 选择结构 2. 主要语句类型 3. 比较操作 4. 交换操作 测试说明 通关代码 测试结果 任务描述 本关任务:从键盘上输入三个数,请按从小到大的顺序排序并打印输出排序后的结果。 编程要求 根据提示…...
简洁安装配置在Windows环境下使用vscode开发pytorch
简洁安装配置在Windows环境下使用vscode开发pytorch 使用anaconda安装pytorch,通过vscode集成环境开发pytorch 下载 anaconda 下载网址,选择对应系统的版本 https://repo.anaconda.com/archive/ windows可以选择Anaconda3-2024.10-1-Windows-x86_64.e…...
conda安装及demo:SadTalker实现图片+音频生成高质量视频
1.安装conda 下载各个版本地址:https://repo.anaconda.com/archive/ win10版本: Anaconda3-2023.03-1-Windows-x86_64 linux版本: Anaconda3-2023.03-1-Linux-x86_64 Windows安装 环境变量 conda -V2.配置conda镜像源 安装pip conda…...
【面试】后端开发面试中常见数据结构及应用场景、原理总结
在后端开发面试中,常见的数据结构包括数组、链表、栈、队列、二叉树、平衡树、堆、图和哈希表等。以下是这些数据结构的总结,包括它们的应用场景、优缺点。 常见数据结构及其应用场景 数据结构应用场景数组存储固定大小的数据集合,如学生成…...
141.《mac m系列芯片安装mongodb详细教程》
文章目录 下载从官网下载安装包 下载后双击解压出文件夹安装文件名修改为 mongodb配置data存放位置和日志log的存放位置启动方式一方式二方式二:输入mongo报错以及解决办法 本人电脑 m2 pro,属于 arm 架构 下载 官网地址: mongodb官网 怎么查看自己电脑应该下载哪个版本,输入…...
Java 23 集合框架详解:ArrayList、LinkedList、Vector
📚 Java 23 集合框架详解:ArrayList、LinkedList、Vector 在 Java 集合框架中,ArrayList、LinkedList 和 Vector 是三种最常用的 List 接口实现类。它们都可以存储有序的、可重复的元素,但它们在 底层实现、性能 和 多线程安全 等…...
03、MySQL安全管理和特性解析(DBA运维专用)
03、MySQL安全管理和特性解析 本节主要讲MySQL的安全管理、角色使用、特定场景下的数据库对象、各版本特性以及存储引擎 目录 03、MySQL安全管理和特性解析 1、 用户和权限管理 2、 MySQL角色管理 3、 MySQL密码管理 4、 用户资源限制 5、 忘记root密码处理办法 6、 SQ…...
创建型模式5.单例模式
创建型模式 工厂方法模式(Factory Method Pattern)抽象工厂模式(Abstract Factory Pattern)建造者模式(Builder Pattern)原型模式(Prototype Pattern)单例模式(Singleto…...
用户界面软件02
基于表单的用户界面 在“基于表单的用户界面”里面,用户开始时选中某个业务处理(模块),然后应用程序就使用一系列的表单来引导用户完成整个处理过程。大型机系统上的大部分用户界面都是这样子的。[Cok97]中有更为详细的讨论。 面…...
VTK 鼠标+键盘重构
1、鼠标事件 如果有鼠标事件处理等相应的需求,可以重写该事件。 void OnMouseMove() override; //鼠标移动事件 void OnLeftButtonDown() override;//左键按下事件 void OnLeftButtonUp() override;//左键抬起事件 void OnMiddleButtonDown() override;//滚轮按下事件 …...
go语言处理JSON数据详解
一、结构体与json之间的转换 Go语言处理JSON数据通常涉及到将JSON数据解析成Go结构体,或者将Go结构体序列化为JSON格式。Go提供了内置的encoding/json包来实现这些操作。下面详细介绍如何在Go中处理JSON数据。 1. Go结构体与JSON映射 Go语言的encoding/json包可以将JSON数据…...
基于gin一个还算比较优雅的controller实现
看了两天时间的go,对于go的编码风格还不是很了解,但是了解到go并未有Java那样成体系的编码风格规范,所以自己浅尝试了一下,风格无对错,欢迎交流讨论~ controller层: package …...
在rocky linux 9.5上在线安装 docker
前面是指南,后面是日志 sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install docker-ce docker-ce-cli containerd.io -y docker version sudo systemctl start docker sudo systemctl status docker …...
渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止
<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet: https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...
Opencv中的addweighted函数
一.addweighted函数作用 addweighted()是OpenCV库中用于图像处理的函数,主要功能是将两个输入图像(尺寸和类型相同)按照指定的权重进行加权叠加(图像融合),并添加一个标量值&#x…...
[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?
论文网址:pdf 英文是纯手打的!论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误,若有发现欢迎评论指正!文章偏向于笔记,谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...
el-switch文字内置
el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...
UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)
UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中,UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化…...
关键领域软件测试的突围之路:如何破解安全与效率的平衡难题
在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件,这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下,实现高效测试与快速迭代?这一命题正考验着…...
听写流程自动化实践,轻量级教育辅助
随着智能教育工具的发展,越来越多的传统学习方式正在被数字化、自动化所优化。听写作为语文、英语等学科中重要的基础训练形式,也迎来了更高效的解决方案。 这是一款轻量但功能强大的听写辅助工具。它是基于本地词库与可选在线语音引擎构建,…...
使用Matplotlib创建炫酷的3D散点图:数据可视化的新维度
文章目录 基础实现代码代码解析进阶技巧1. 自定义点的大小和颜色2. 添加图例和样式美化3. 真实数据应用示例实用技巧与注意事项完整示例(带样式)应用场景在数据科学和可视化领域,三维图形能为我们提供更丰富的数据洞察。本文将手把手教你如何使用Python的Matplotlib库创建引…...
腾讯云V3签名
想要接入腾讯云的Api,必然先按其文档计算出所要求的签名。 之前也调用过腾讯云的接口,但总是卡在签名这一步,最后放弃选择SDK,这次终于自己代码实现。 可能腾讯云翻新了接口文档,现在阅读起来,清晰了很多&…...
