NPOI操作Excel(三)--解析Excel
通过前面两篇的基础学习,我们对NPOI有了一定了了解,下面就开始进入实战,解析下面格式的Excel(下面只是列举了几个例子),并保存入库



首先我们先分析一下,要解析这样的Excel,需要把指标【橘色背景和蓝色背景】(作为指标入库)、科目【棕色背景和黄色背景】(作为X轴入库)、数据【乳白色背景和白色背景】(作为Y轴入库)的数据分开入库。
第一张图我们得到的指标毫无疑问应该是第三行从第二列开始到最后一列的数据,而第二张图我们得到的指标应该是非金融企业部门-使用、非金融企业部门-来源、金融机构部门-使用、金融机构部门-来源,以此类推,我们要想取到这样的数据,首先需要把合并行的单元格填充、然后把合并列的数据合并,我们可以通过二维数组来实实现。
由于每个Excel的格式不一样,指标数据的行数,列数也不一样,所以我们要想把数据区分开只能通过背景颜色,把三部分是数据分开并放到三个二维数组里,然后解析入库,由于Excel的背景颜色存在不一样,所以不能写死,通过观察我们可以发现,每个Excel都是从指标行开始有背景颜色到数据行开始变背景颜色,这样我们就可以区分开来,到这里相信聪明的你已经知道怎么做了,下面我们就开始实现吧
1、获取Excel的扩展名并创建工作簿,如果是xls创建HSSFWorkbook工作簿,如果是xlxs创建XSSFWorkbook工作簿
public static void ReadFromExcelFile(string filePath)
{
IWorkbook wk = null;
string extension = System.IO.Path.GetExtension(filePath);//GetExtension获取Excel的扩展名
try
{
FileStream fs = File.OpenRead(filePath);
if (extension.Equals(".xls"))
{
wk = new HSSFWorkbook(fs); //把xls文件中的数据写入wk中
}
else
{
wk = new XSSFWorkbook(fs);//把xlsx文件中的数据写入wk中
}
fs.Close();
sheet = wk.GetSheetAt();//读取当前表数据 GetIndexRow();//获取【指标、科目、数据】的行数列数
ReadData();//读数据并保存到数组中
SaveData();//解析数组数据并保存入库
}
catch (Exception e)
{
Console.WriteLine(e.Message); //只在Debug模式下才输出
}
}
2、获取指标从哪行开始
for (int i = ; i < sheet.LastRowNum; i++)//sheet.LastRowNum当前表的行数
{
IRow row = sheet.GetRow(i); //读取当前行数据
if (row != null)
{
if (row.GetCell() != null) //读取该行的第1列数据
{
ICellStyle style = row.GetCell().CellStyle;//当前行第一列的样式
row.GetCell().SetCellType(CellType.String);//把第一行第一列的值类型转换成string类型
short GroundColor = style.FillForegroundColor;//获取当前行第一列的背景色
if (i == )//若或i=0说明是第一行,没有背景色的
{
Title = row.GetCell().StringCellValue;//获取第一行第一列的值即标题的值
TitleColor = GroundColor;//第一行第一列背景色的值付给TitleColor
continue;
}
else//如果不是第一行
{
if (GroundColor == TitleColor)
{
if (row.GetCell().StringCellValue.Contains("单位"))
{
IndexUnit = row.GetCell().StringCellValue.Replace("单位:", "").Replace("单位:", "");
continue;
}
}
else if (GroundColor != TitleColor && IndexColor == )//如果GroundColor不等于TitleColor说明改行是指标行
{
IndexColor = GroundColor;// 把GroundColor的值赋值给IndexColor
IndexStart = i;//记录改行,改行是指标行的起始行
break;
}
}
}
}
}
3、获取指标从哪行结束
for (int i = IndexStart + ; i < sheet.LastRowNum; i++)
{
IRow row = sheet.GetRow(i); //读取当前行数据
if (row != null)
{
if (row.GetCell() != null) //读取该行的第1列数据
{
ICellStyle style = row.GetCell().CellStyle;
short GroundColor = style.FillForegroundColor;
if (IndexColor != GroundColor)
{
LeftDataColor = GroundColor;
IndexEnd = i - ;
break;
}
}
}
}
4、获取数据从哪行开始到哪行结束
for (int i = IndexEnd + ; i < sheet.LastRowNum; i++)
{
DataRowStart = IndexEnd + ;//数据开始行
IRow row = sheet.GetRow(i); //读取当前行数据
if (row != null)
{
if (row.GetCell() != null) //读取该行的第1列数据
{
ICellStyle style = row.GetCell().CellStyle;
short GroundColor = style.FillForegroundColor;
if (LeftDataColor != GroundColor)
{
DataRowEnd = i - ;//数据结束行
break;
}
}
}
}
5、获取科目【左侧】的列数
if (sheet.GetRow(IndexEnd + ) != null)
{
for (int i = ; i < sheet.GetRow(IndexEnd + ).LastCellNum; i++)
{
if (sheet.GetRow(IndexEnd + ).GetCell(i) != null)
{
ICellStyle style = sheet.GetRow(IndexEnd + ).GetCell(i).CellStyle;
short GroundColor = style.FillForegroundColor;
sheet.GetRow(IndexEnd + ).GetCell(i).SetCellType(CellType.String);
if (GroundColor != LeftDataColor)
{
DataLeftCell = i;//科目的列数
break;
}
}
}
}
6、把数据保存到数组中【指标数组】
string[,] IndexArray = new string[IndexEnd-IndexStart+, sheet.GetRow().LastCellNum - DataLeftCell];//指标 //循环指标行
for (int r = IndexStart; r <= IndexEnd; r++)
{
IRow row = sheet.GetRow(r); //读取当前行数据
if (row != null)
{
for (int c = DataLeftCell; c <= row.LastCellNum - DataLeftCell; c++)
{
if (row.GetCell(c) != null)
{
row.GetCell(c).SetCellType(CellType.String);
#region 判断是否是合并单元格
if (string.IsNullOrEmpty(row.GetCell(c).StringCellValue))
{
ICell cell = row.GetCell(c);
Dimension dimension = new Dimension();
if (IsMergedRegions.IsMergeCell(cell, out dimension))//如果是空判断是否是合并单元格
{
IndexArray[r - IndexStart, c- DataLeftCell] = dimension.DataCell.StringCellValue;//如果是取合并单元格的值
}
else
{
IndexArray[r - IndexStart, c- DataLeftCell] = row.GetCell(c).StringCellValue;//否则取改单元格本身的值
}
}
else
{
IndexArray[r - IndexStart, c- DataLeftCell] = row.GetCell(c).StringCellValue;
}
#endregion
}
}
}
}
7、把数据保存到数组中【科目数组】
string[,] LeftDataArray = new string[DataRowEnd-DataRowStart+, DataLeftCell];//科目
for (int r = DataRowStart; r <= DataRowEnd; r++)
{
IRow row = sheet.GetRow(r); //读取当前行数据
if (row != null)
{
for (int c = ; c < DataLeftCell; c++)
{
if (row.GetCell(c) != null)
{
row.GetCell(c).SetCellType(CellType.String); #region 判断是否是合并单元格
if (string.IsNullOrEmpty(row.GetCell(c).StringCellValue))
{
ICell cell = row.GetCell(c);
Dimension dimension = new Dimension();
if (IsMergedRegions.IsMergeCell(cell, out dimension))
{
LeftDataArray[r - DataRowStart, c] = dimension.DataCell.StringCellValue;
}
else
{
LeftDataArray[r - DataRowStart, c] = row.GetCell(c).StringCellValue;
}
}
else
{
LeftDataArray[r - DataRowStart, c] = row.GetCell(c).StringCellValue;
}
#endregion
}
}
}
}
8、把数据保存到数组中【数据数组】
string[,] RightDataArray= new string[DataRowEnd - DataRowStart + , sheet.GetRow().LastCellNum - DataLeftCell];//数据
for (int r = DataRowStart; r <= DataRowEnd; r++)
{
IRow row = sheet.GetRow(r); //读取当前行数据
if (row != null)
{
for (int c = DataLeftCell; c < row.LastCellNum; c++)
{
if (row.GetCell(c) != null)
{
row.GetCell(c).SetCellType(CellType.String);
RightDataArray[r - DataRowStart, c- DataLeftCell] = row.GetCell(c).StringCellValue;
}
}
}
}
9、解析数组保存数据
private static void SaveData()
{
//IndexModel im = new IndexModel();
DataModel dm = new DataModel();
for (int ic = ; ic < sheet.GetRow().LastCellNum - DataLeftCell ; ic++)//循环指标列
{
dm.IndexName = null;
dm.IndexCode = IndexCode++.ToString().PadLeft(, '');
#region 获取指标名称
for (int ir = ; ir < IndexEnd - IndexStart + ; ir++)
{
if (IndexArray[ir, ic] != null)
{
if (dm.IndexName == null)
{
dm.IndexName = IndexArray[ir, ic];
}
else
{
if (!dm.IndexName.Contains(IndexArray[ir, ic]))
{
dm.IndexName = dm.IndexName + "_" + IndexArray[ir, ic];//同一列字符串拼接
}
}
}
}
#endregion
//循环得右侧数据
for (int rr = ; rr < DataRowEnd - DataRowStart + ; rr++)//循环右侧数据的行
{
#region 右侧数据
if (RightDataArray[rr, ic] != null)
{
dm.IndexYValue = RightDataArray[rr, ic];
}
#endregion
dm.IndexXValue = null;
//循环得左侧数据
for (int lc = ; lc < DataLeftCell; lc++)
{
if (LeftDataArray[rr, lc] !=null)
{
if (dm.IndexXValue == null)
{
dm.IndexXValue = LeftDataArray[rr, lc];
}
else
{
if (!dm.IndexXValue.Contains(LeftDataArray[rr, lc]))
{
dm.IndexXValue = dm.IndexXValue + "_" + LeftDataArray[rr, lc];
}
}
}
}
Console.WriteLine($"指标名称:{dm.IndexName} 指标编码:{dm.IndexCode} IndexXValue:{dm.IndexXValue} IndexYValue:{dm.IndexYValue}");
}
}
}
10、上面用到的方法IsMergeCell判断是否是合并单元格
/// <summary>
/// 判断指定单元格是否为合并单元格,并且输出该单元格的维度
/// </summary>
/// <param name="cell">单元格</param>
/// <param name="dimension">单元格维度</param>
/// <returns>返回是否为合并单元格的布尔(Boolean)值</returns>
public static bool IsMergeCell(this ICell cell, out Dimension dimension)
{
return cell.Sheet.IsMergeCell(cell.RowIndex, cell.ColumnIndex, out dimension);
}
NPOI操作Excel(三)--解析Excel的更多相关文章
- Java上传下载excel、解析Excel、生成Excel
在软件开发过程中难免需要批量上传与下载,生成报表保存也是常有之事,最近集团门户开发用到了Excel模版下载,Excel生成,圆满完成,对这一知识点进行整理,资源共享,有不足之处还望批评指正,文章结尾提 ...
- oracle xmltype导入并解析Excel数据 (三)解析Excel数据
包声明 create or replace package PKG_EXCEL_UTILS is -- Author: zkongbai-- Create at: 2016-07-06-- Actio ...
- 使用POI做的一个生成Excel的工具类。包含了导出Excel和解析Excel方法
PoiExcelUtils.java /** * */ package com.common.office; import java.io.File; import java.io.FileInput ...
- POI使用:用poi接口不区分xls/xlsx格式解析Excel文档(41种日期格式解析方法,5种公式结果类型解析方法,3种常用数值类型精度控制办法)
一.使用poi解析excel文档 注:全部采用poi接口进行解析,不需要区分xls.xlsx格式,不需要判断文档类型. poi中的日期格式判断仅支持欧美日期习惯,对国内的日期格式并不支持判断,怎么办? ...
- Java解析Excel之应用Reflection等技术实现动态读取
目录树 背景 技术选型 问题分析 技术要点及难点分析 源码分析 测试用例 背景 Tip:因为产品提的需求我都开发完了,进行了项目提测:前天老大走过来说:你用spring-boot开发一个解析Excel ...
- NPOI操作EXCEL(三)——反射机制进行excel表格数据的解析
我们先来回忆回忆上篇文章讲到的通过xml配置文件实现excel批量模板解析的整体思路: 1.对每个excel模板制定xml配置规则集,实现xml配置文件的解析服务 2.为每个excel模板制定DTO, ...
- NPOI操作EXCEL(六)——矩阵类表头EXCEL模板的解析
哈哈~~~很高兴还活着.总算加班加点的把最后一类EXCEL模板的解析做完了... 前面几篇文章介绍了博主最近项目中对于复杂excel表头的解析,写得不好,感谢园友们的支持~~~ 今天再简单讲诉一下另一 ...
- NPOI操作EXCEL(二)——大量不同模板时设计方式
上一篇文章介绍了一些NPOI的基础接口,我们现在就来看看具体怎么用NPOI来解析一个EXCEL. 博主现在有这么一堆excel需要解析数据入库: 当然这只是员工的简要模板,还有很多其他的模板.我们可以 ...
- NPOI操作EXCEL(四)——反射机制批量导出excel文件
前面我们已经实现了反射机制进行excel表格数据的解析,既然有上传就得有下载,我们再来写一个通用的导出方法,利用反射机制实现对系统所有数据列表的筛选结果导出excel功能. 我们来构想一下这样一个画面 ...
随机推荐
- 【转载分享】 JLINKv9在迅为iTOP-4412精英板上的应用
本文转自:https://www.amobbs.com/thread-5680586-1-1.html 很多人买迅为iTop4412精英板,在Android或Linux+Qt跑起来后学习开发调试应用程 ...
- 【转载】奇异值分解(SVD)计算过程示例
原文链接:奇异值分解(SVD)的计算方法 奇异值分解是线性代数中一种重要的矩阵分解方法,这篇文章通过一个具体的例子来说明如何对一个矩阵A进行奇异值分解. 首先,对于一个m*n的矩阵,如果存在正交矩阵U ...
- springboot中spring.profiles.active来引入多个properties文件 & Springboot获取容器中对象
1. 引入多个properties文件 很多时候,我们项目在开发环境和生成环境的环境配置是不一样的,例如,数据库配置,在开发的时候,我们一般用测试数据库,而在生产环境的时候,我们是用正式的数据, ...
- Spring3.2+mybatis3.2+Struts2.3整合
1.Spring3.2不能用于JDK1.8,只能用于JDK1.7.JDK1.8用spring4.0. 2.导入的jar包 3.目录结构: 4.配置Spring 配置数据库信息: <?xml ve ...
- 【转】Win10下python3和python2多版本同时安装并解决pip共存问题
[转]Win10下python3和python2多版本同时安装并解决pip共存问题 特别说明,本文是在Windows64位系统下进行的,32位系统请下载相应版本的安装包,安装方法类似. 使用pytho ...
- python opencv3添加opencv-contrib
不需要编译或其他操作,只需一句话安装第三方库利用sift等特征提取算法: sudo pip3 install opencv-contrib-python 附网站:https://pypi.python ...
- CSRFGuard工具介绍
理解CSRFGuard的基础:http://www.runoob.com/jsp/jsp-tutorial.html 1:您需要做的第一件事是将OWASP.CSRFARGAD.JAR库复制到类路径中. ...
- 51nod--1264 线段相交 (计算几何基础, 二维)
题目: 1264 线段相交 基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题 收藏 关注 给出平面上两条线段的两个端点,判断这两条线段是否相交(有一个公共点或有部分重合认为 ...
- mysql优化——show processlist命令详解
SHOW PROCESSLIST显示哪些线程正在运行 不在mysql提示符下使用时用mysql -uroot -e 'Show processlist' 或者 mysqladmin pro ...
- JVM·垃圾收集器与内存分配策略之对象是否可被回收!
1.判断对象已经死去/不再被引用. 1.1.引用计数算法:给对象添加引用计数器,有个地方引用就+1,引用失效就-1.任何时刻,引用为0,即判断对象死亡. 1.1.1.优点:实现 ...