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功能. 我们来构想一下这样一个画面 ...
随机推荐
- ubuntu16.04配置anaconda环境
0 - 下载安装包 推荐到清华镜像下载.我选择的是Anaconda3-5.1.0-Linux-x86_64.sh. 1 - 安装Anaconda 然后切换到安装包目录,执行下面命令,期间一直按回车或者 ...
- 微信小程序-用户拒绝授权使用 wx.openSetting({}) 重新调起授权用户信息
场景模拟:用户进入微信小程序-程序调出授权 选择拒绝之后,需要用到用户授权才能正常使用的页面,就无法正常使用了. 解决方法:在用户选择拒绝之后,弹窗提示用户 拒绝授权之后无法使用,让用户重新授权(微信 ...
- JAVA并行异步编程,线程池+FutureTask
java 在JDK1.5中引入一个新的并发包java.util.concurrent 该包专门为java处理并发而书写. 在java中熟悉的使用多线程的方式为两种?继续Thread类,实现Runnal ...
- Linux 应用层的时间编程【转】
转自:https://blog.csdn.net/chinalj2009/article/details/21223681 浅析 Linux 中的时间编程和实现原理,第 1 部分: Linux 应用层 ...
- 普通函数跟箭头函数中this的指向问题
箭头函数和普通函数的区别如下. 普通函数:根据调用我的人(谁调用我,我的this就指向谁) 箭头函数:根据所在的环境(我再哪个环境中,this就指向谁) 一针见血式总结: 普通函数中的this: 1. ...
- 设计模式C++学习笔记之二(Proxy代理模式)
代理,一看名字就知道这只是个中介而已,真实的执行者在代理的后面呢.cbf4life在他的书里提的例子也很有趣,更详细的内容及说明可以参考原作者博客:cbf4life.cnblogs.com.现在贴 ...
- python类的使用
下面是一个员工类的创建及类对象的创建实例: #!/usr/bin/python # -*- coding: UTF-8 -*-class Employee: empCount = 0 def __in ...
- LIght OJ 1179
题意: 约瑟夫环问题, 给你N 个人, 没K个出队, 问最后剩下的人的编号. 思路: 直接模拟会T, 对于N个人 , 是一个约瑟夫环问题, 当第一个人出队后, (标号一定为 k % n -1) 剩下的 ...
- su命令
作用: 切换 用户 选项: -:可以更改当前目录为切换用户的家目录 使用: # 切换用户,当前所在用户目录不变 su 用户名 # 切换用户,当前所在目录改为切换对象的家目录 /home/用户名 su ...
- pip的常用命令
前言 pip作为Python的御用包管理工具有着强大的功能,但是许多命令需要我们使用的时候借助搜索引擎查找(尤其是我), 于是我想将我使用到的命令整合下来,以后不用麻烦去找了,也希望能给你带来帮助.文 ...