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功能. 我们来构想一下这样一个画面 ...
随机推荐
- Hadoop环境准备
1. 集群简介 HADOOP集群具体来说包含两个集群:HDFS集群和YARN集群,两者逻辑上分离,但物理上常在一起. HDFS集群负责海量数据的存储,集群中的角色主要有: NameNode.DataN ...
- Springboot实体类转JSON报错Could not find acceptable representation & 设置访问项目根路径的默认欢迎页面
=================实体类转JSON报错的解决办法============= 之前在springmvc的时候也报过这个错,原因以及springmvc中解决办法参考:https://www ...
- yum install mariadb安装数据库开启不了
centos7内置的MySQL镜像已经放弃Oracle公司的MySQL,改用MySQL的分支数据库mariaDB,使用以下安装mariadb: yum install mariadb 然后使用命令sy ...
- Python-字符串的常用操作
name = "my name is irving and i am 22 years old!" #开头字母大写 print(name.capitalize()) #统计某字符个 ...
- jquery获取当前按钮、截取字符串、字符串拼接、动态循环添加元素
截取字符串:字符串拼接:动态循环添加元素:获取当前按钮: {data : null, render: function(data, type, row ) { var loginName = $(&q ...
- [JLOI2011]飞行路线 不同的算法,不同的悲伤
题目 :BZOJ2763 洛谷P4568 [JLOI2011]飞行路线 一道最短路的题目,想想写个题解也不错(好久没写题解了_(:з」∠)_) 然后这道题中心思路是dijikstra处理最短路,所以没 ...
- bash的快捷键、特殊参数、历史命令、相关文件
bash快捷键 Emacs风格 ctrl+p: 方向键 上 ↑ ctrl+n: 方向键下 ↓ ctrl+b: 方向键 ← alt+f: 光标右移一个单词 ctrl+f :方向键 → alt+b: 光标 ...
- 前端 ----jQuery的属性操作
04-jQuery的属性操作 jquery的属性操作模块分为四个部分:html属性操作,dom属性操作,类样式操作和值操作 html属性操作:是对html文档中的属性进行读取,设置和移除操作.比如 ...
- [javascript]multipart/form-data上传格式表单自定义创建
<!DOCTYPE html> <html> <head> <title></title> </head> <body&g ...
- 用sitemap做主页的菜单栏
首先打开vs--> 新建项-->选择站点地图. 站点地图建好了 其中具体的节点根据自己情况配好就行. 接下来是两个非常重要的类: using System.Collections.Gen ...