java使用poi读取跨行跨列excel

article/2025/9/13 20:38:11

java使用poi读取跨行跨列excel

  • 1.需求背景
  • 2.实现思路分析
  • 3.重要代码片码说明
  • 4.完整的代码类如下:
  • 5.完整的demo代码提供如下
  • 6.demo执行结果


1.需求背景

最近有一个工作任务是用户提供了一个基础的excel文件,要求首先将excel中的数据解析并入库,然后再做后续的一些业务处理,因此涉及到excel的数据读取,正常如果是一行一行数据的excel的读取,还是比较简单,但用户提供的数据涉及跨行跨列问题,就稍有点麻烦,比如数据样例如下:
在这里插入图片描述
需要将跨行跨列数据也读取出来最后进行入库处理,比如要解析成如下的数据并入库。
在这里插入图片描述

2.实现思路分析

首先以上的excel样例数据中,第一列共2个跨行区域,天河区和番禺区,每个跨行区域中在第二列又存在子跨行数据的问题,查看了poi的api,如获取到excel的工作表sheet后(通过如XSSFSheet sheet = xssfWorkbook.getSheetAt(0);获取),有个sheet.getNumMergedRegions()方法,此方法会返回excel中的所有关于合并单元格的信息,每个信息中包含了这个合并单元格的开始行,结束行,开始列,结束列。合并单元格的数据能通过第一行第一列获取到。

因此,可以采用一个hashmap将以上的合并单元格的信息进行预先读取,将存在合并单元格的列记录入put进hashmap, 如key为行号+下划线+列号组成,value为记录了合并单元格的起始行号和起始列号组成的数组

有了上面的hashmap之后,就可以按常规的遍历excel的行,然后再遍历excel的列进行数据的读取处理了,对每一个单元格进行判断,判断此单元格的行号+列号组成的key是不是在hashmap中存在,存在的话说明是一个合并单元格,读取数据就从value中取出合并单元格的起始行号和起始列号,通过合并单元格的起始行号和起始列号组成的单元格读取真正的数据(合并单元格的组据在首行和首列,其他行和列是空白)比如,以下的510075的数据,要通过读B2这一格才能读到数据
在这里插入图片描述

3.重要代码片码说明

  1. 获取存在合并单元格的列记录并以行号列号为key组成的map
	//将存在合并单元格的列记录入put进hashmap并返回public Map<String,Integer[]> getMergedRegionMap(Sheet sheet){Map<String,Integer[]> result = new HashMap<String,Integer[]>();//获取excel中的所有合并单元格信息int sheetMergeCount = sheet.getNumMergedRegions();//遍历处理for (int i = 0; i < sheetMergeCount; i++) {//拿到每个合并单元格,开始行,结束行,开始列,结束列CellRangeAddress range = sheet.getMergedRegion(i);  int firstColumn = range.getFirstColumn();  int lastColumn = range.getLastColumn();  int firstRow = range.getFirstRow();  int lastRow = range.getLastRow();//构造一个开始行和开始列组成的数组Integer[] firstRowNumberAndCellNumber = new Integer[]{firstRow,firstColumn};//遍历,将单元格中的所有行和所有列处理成由行号和下划线和列号组成的key,然后放在hashmap中for(int currentRowNumber = firstRow; currentRowNumber <= lastRow; currentRowNumber++) {for(int currentCellNumber = firstColumn; currentCellNumber <= lastColumn; currentCellNumber ++) {result.put(currentRowNumber+"_"+currentCellNumber, firstRowNumberAndCellNumber);}}}return result;}

4.完整的代码类如下:

package poitest;import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class ExcelMergegionReadTest {private Logger logger = LoggerFactory.getLogger(this.getClass());public static void main(String[] args) {new ExcelMergegionReadTest().excelRegionReadStart();}public void excelRegionReadStart(){long start = System.currentTimeMillis();InputStream inputStream = null;// 输入流对象XSSFWorkbook xssfWorkbook = null; //工作簿try {inputStream = this.getClass().getClassLoader().getResourceAsStream("datatest.xlsx");//定义工作簿xssfWorkbook = new XSSFWorkbook(inputStream);//获取第一个sheetXSSFSheet sheet = xssfWorkbook.getSheetAt(0);//获取合并单元格信息的hashmapMap<String,Integer[]> mergedRegionMap = getMergedRegionMap(sheet);//拿到excel的最后一行的索引int lastRowNum = sheet.getLastRowNum();//从excel的第二行索行开始,遍历到最后一行(第一行是标题,直接跳过不读取)for(int i = 1; i<=lastRowNum ; i++) {//拿到excel的行对象XSSFRow row = sheet.getRow(i);//获取excel的行中有多个列int cellNum = row.getLastCellNum();//对每行进行列遍历,即一列一列的进行解析for(int j=0; j<cellNum; j++) {//拿到了excel的列对象Cell cell = row.getCell(j);//将列对象的行号和列号+下划线组成key去hashmap中查询,不为空说明当前的cell是合并单元列Integer[] firstRowNumberAndCellNumber = mergedRegionMap.get(i+"_"+j);//如果是合并单元列,就取合并单元格的首行和首列所在位置读数据,否则就是直接读数据if(firstRowNumberAndCellNumber != null) {XSSFRow rowTmp = sheet.getRow(firstRowNumberAndCellNumber[0]);Cell cellTmp = rowTmp.getCell(firstRowNumberAndCellNumber[1]);System.out.println(getCellValue(cellTmp));}else{System.out.println(getCellValue(cell));}}System.out.println("================================================");}} catch (Exception e) {logger.error("error",e);} finally {// 关闭文件流if (inputStream != null) {try {inputStream.close();} catch (IOException e) {logger.error("error",e);}}// 关闭工作簿if(xssfWorkbook != null){try {xssfWorkbook.close();} catch (IOException e) {logger.error("error",e);}}}long end = System.currentTimeMillis();System.out.println("spend ms: " + (end - start) + " ms.");}//将存在合并单元格的列记录入put进hashmap并返回public Map<String,Integer[]> getMergedRegionMap(Sheet sheet){Map<String,Integer[]> result = new HashMap<String,Integer[]>();//获取excel中的所有合并单元格信息int sheetMergeCount = sheet.getNumMergedRegions();//遍历处理for (int i = 0; i < sheetMergeCount; i++) {//拿到每个合并单元格,开始行,结束行,开始列,结束列CellRangeAddress range = sheet.getMergedRegion(i);  int firstColumn = range.getFirstColumn();  int lastColumn = range.getLastColumn();  int firstRow = range.getFirstRow();  int lastRow = range.getLastRow();//构造一个开始行和开始列组成的数组Integer[] firstRowNumberAndCellNumber = new Integer[]{firstRow,firstColumn};//遍历,将单元格中的所有行和所有列处理成由行号和下划线和列号组成的key,然后放在hashmap中for(int currentRowNumber = firstRow; currentRowNumber <= lastRow; currentRowNumber++) {for(int currentCellNumber = firstColumn; currentCellNumber <= lastColumn; currentCellNumber ++) {result.put(currentRowNumber+"_"+currentCellNumber, firstRowNumberAndCellNumber);}}}return result;}/**   * 获取单元格的值   * @param cell   * @return   */    public String getCellValue(Cell cell){    if(cell == null) return "";    if(cell.getCellType() == CellType.STRING){    return cell.getStringCellValue();    }else if(cell.getCellType() == CellType.BOOLEAN){    return String.valueOf(cell.getBooleanCellValue());    }else if(cell.getCellType() == CellType.FORMULA){    return cell.getCellFormula() ;    }else if(cell.getCellType() == CellType.NUMERIC){    return String.valueOf(cell.getNumericCellValue());    }return "";}  }

5.完整的demo代码提供如下

github: https://github.com/jxlhljh/excelmergeregiontest.git

gitee: https://gitee.com/jxlhljh/excelmergeregiontest.git

6.demo执行结果

在这里插入图片描述


http://chatgpt.dhexx.cn/article/6P9A9r7f.shtml

相关文章

itextpdf 表格跨行跨列与可视化图表

文章目录 itextpdf 表格跨行跨列与可视化图表效果图普通表格一&#xff08;表头背景色)普通表格二 &#xff08;隔列变色)表格跨行跨列可视化图表 使用示例普通表格一&#xff08;表头背景色&#xff09;普通表格二 &#xff08;隔列变色&#xff09;表格跨行跨列可视化图表 工…

EasyUI Datagrid跨行跨列的需求

常规开发后台管理系统中遇到的列表查询&#xff0c;都是用到最基本的数据网格&#xff0c;不包括单元格合并&#xff0c;多列页眉&#xff0c;冻结列和页脚等需求。类似如下这个列表 实现也很简单&#xff0c;引入相关的js、css文件&#xff0c;html标签定义展示的列&#xff0…

html布局 跨行,3种方案实现跨行或跨列布局

今天要讲解的这个问题是之前一位小伙伴在群里请教的提问,是关于跨行跨列的布局,如下图是他要实现的具体布局,看似是最简单的二维布局,实际透露出整个CSS的发展方向。向前可以考察对兼容性的处理功底,向后可以考察对CSS新特性的洞察能力。可攻可受,攻守兼备。 如果对此问题…

HTML表格属性跨列,HTML表格的使用 与 跨行跨列

表格的基本语法&#xff1a; 第一个单元格的内容第二个单元格的内容第一个单元格的内容第二个单元格的内容 创建表格一般分为下面四个步骤 1.创建表格标签table 2.在表格标签table创建行标签tr可以有多行 3.在第一行标签tr里创建单元格标签th可以创建表格标题 4.在行标签tr里创…

EXCEL 跨列居中

居中到箭头所指的位置。 先选中三个单元格&#xff0c;打开格式&#xff0c;找到对齐里的跨列居中就行。 结果&#xff1a;

redis数据结构应用

介绍redis数据结构应用场景

redis底层都有哪些数据结构?带你了解redis是如何存储数据的

文章目录 写在前面键值对的存储——哈希哈希冲突redis解决哈希冲突过多的方式——rehash 双向链表数组压缩链表压缩链表的连锁更新压缩列表的缺陷整数数组和压缩列表在查找时间复杂度方面并没有很大的优势&#xff0c;那为什么 Redis 还会把它们作为底层数据结构呢&#xff1f;…

redis底层数据结构(redis底层存储结构、源码分析)

文章目录 前言一、redis为什么快&#xff1f;二、redis的底层数据结构2.1、redis的底层存储的扩容机制2.1.1、扩容时间2.1.2、扩容多大2.1.3、扩容后的rehash2.1.4、何时进行rehash2.1.5、俩hashtable访问那个呢&#xff1f; 2.2、redis的key的底层数据类型(sds)2.2.1、sds(Sim…

redis底层数据结构 -String

redis包含5种常用数据结构 String 、List、Hash、Set 、Zset 基础铺垫 以set为例 redis其实可以理解为 K-V数据库&#xff0c;因此对每个键值对都会有一个 dictEntry&#xff0c;里面存储了指向 Key 和 Value 的指针&#xff1b;next 指向下一个 dictEntry&#xff0c;与本 …

Redis-常用数据结构

Redis常用数据结构 Redis提供了一些数据结构供我们往Redis中存取数据&#xff0c;最常用的的有5种&#xff0c;字符串&#xff08;String&#xff09;、哈希(Hash)、列表&#xff08;list&#xff09;、集合&#xff08;set&#xff09;、有序集合&#xff08;ZSET&#xff09…

「Redis数据结构」集合对象(Set)

「Redis数据结构」集合对象&#xff08;Set&#xff09; 文章目录 「Redis数据结构」集合对象&#xff08;Set&#xff09;一、概述二、结构三、编码转换四、小结 一、概述 Set是Redis中的单列集合&#xff0c;其特点为不保证有序性、保证元素唯一、可以求交集、并集、差集。 …

图解redis五种数据结构底层实现

redis有五种基本数据结构&#xff1a;字符串、hash、set、zset、list。但是你知道构成这五种结构的底层数据结构是怎样的吗&#xff1f;今天我们来花费五分钟的时间了解一下。(目前redis版本为3.0.6) 动态字符串SDS SDS是"simple dynamic string"的缩写。redis中所…

redis底层数据结构-List

举例分析 创建列表对象 numbers 列表对象有两种底层实现结构 1.压缩列表(zipList)实现的列表对象 压缩列表(zipList)是Redis为了节省内存而开发的&#xff0c;是由一系列特殊编码的连续内存块组成的顺序型数据结构&#xff0c;一个压缩列表可以包含任意多个节点&#xff08;e…

redis中hash数据结构

目录 hash的数据结构ziplist底层实现字典底层实现扩容缩容 引用 hash的数据结构 hash底层数据结构的实现包括两种&#xff1a;ziplist和字典当保存的所有键值对字符串长度小于 64 字节并且键值对数量小于 512 时使用ziplist &#xff0c;否则使用字典的方式 ziplist底层实现 …

redis的五种数据结构

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 前言 一、字符串&#xff08;String&#xff09; 1.SDS的定义 2.SDS与C语言中字符串的区别&#xff08;优点&#xff09; 2.1 获取字符串长度 2.2 防止缓冲区的溢…

「Redis数据结构」哈希对象(Hash)

「Redis数据结构」哈希对象&#xff08;Hash&#xff09; 文章目录 「Redis数据结构」哈希对象&#xff08;Hash&#xff09;一、概述二、编码ZipListHashTable 三、编码转换 一、概述 Redis中hash对象是一个string类型的field和value的映射表&#xff0c;hash特别适合用于存储…

Redis底层数据结构(图文详解)

目录 前言 Redis为什么要使用2个对象&#xff1f;两个对象的好处 redisObject对象解析 String 类型 1、int 整数值实现 2、embstr 3、raw List 类型 1、压缩链表&#xff1a;ziplist 2、双向链表&#xff1a;linkedlist 3、快速列表&#xff1a;quicklist Hash …

Redis数据结构之hash

对象类数据的存储如果具有较频繁的更新需求操作会显得笨重&#xff0c;这里我们可以用redis的hash数据类型解决。 一、hash类型 新的存储需求&#xff1a;对一系列存储的数据进行编组&#xff0c;方便管理&#xff0c;典型应用存储对象信息 需要的存储结构&#xff1a;一个存储…

Redis数据结构之Zset

文章目录 一、zset数据结构二、跳表skipList什么是跳表&#xff1f;1.跳表的查找2.跳表的插入3.跳表的删除4.跳表的更新 一、zset数据结构 相比于set&#xff0c;sorted set 增加了一个权重参数 score&#xff0c;使得集合中的元素能够按 score 进行有序排列&#xff0c;还可以…

Redis数据结构有哪些

Redis数据结构有哪些 一、Redis数据结构 一、Redis数据结构 Redis是一种基于内存的数据库&#xff0c;并且提供一定的持久化功能&#xff0c;它是一种键值&#xff08;key-value&#xff09;数据库&#xff0c;使用 key 作为 索引找到当前缓存的数据&#xff0c;并且返回给程序…