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功能. 我们来构想一下这样一个画面 ...
随机推荐
- DeepLearning.ai-Week2-Keras tutorial-the Happy House
1 - Import Packages import numpy as np from keras import layers from keras.layers import Input, Dens ...
- 前端必备——js中前端与后台的数据交互全解
只要编程语言能够支持网卡端口的监听和发送,理论上都是可以实现服务器后台设计的.也因此造成了实现后台的语言偏多,而web前端语言以html/css/js为主.所以在这里我们不涉及后台的设计,只介绍在we ...
- 20165237 2017-2018-2 《Java程序设计》第9周学习总结
20165237 2017-2018-2 <Java程序设计>第9周学习总结 教材学习内容总结 1.URL类是java.net包中的一个重要的类,URL的实例封装着一个统一资源定位符(Un ...
- AutoML总结
原文:JxKing的博客 | JxKing Blog 前言 AutoML是指尽量不通过人来设定超参数,而是使用某种学习机制,来调节这些超参数.这些学习机制包括传统的贝叶斯优化,多臂老虎机(multi- ...
- Navicat for MySQL 12中文版 破解流程
1.下载 Keygen_Patch 软件 下载地址 pass: saxz 2.启动 Keygen_Patch 软件 3.提示破解成功了,先别着急 4.运行 Navica 软件,输入注册码 5.断网 ...
- 【转】Python之向日志输出中添加上下文信息
[转]Python之向日志输出中添加上下文信息 除了传递给日志记录函数的参数(如msg)外,有时候我们还想在日志输出中包含一些额外的上下文信息.比如,在一个网络应用中,可能希望在日志中记录客户端的特定 ...
- 从url(地址栏)获取参数:Jquery中getUrlParam()方法的使用
我想要获取如下id 如下代码(传参要加问好!!) function getUrlParam(id) { var regExp = new RegExp('([?]|&)' + id+ '=([ ...
- 64位进程调用32位dll的解决方法 / 程序64位化带来的问题和思考
最近做在Windows XP X64,VS2005环境下做32位程序编译为64位程序的工作,遇到了一些64位编程中可能遇到的问题:如内联汇编(解决方法改为C/C++代码),long类型的变化,最关键的 ...
- epoll机制详解
epoll机制详解 大牛的详解 epoll详解 什么是epoll? epoll是为处理大批量句柄而作了改进的poll, 是性能最好的多路I/O就绪通知方法; 只有三个系统调用: epoll_creat ...
- 在Visual Studio中使用C++创建和使用DLL
[什么是DLL(动态链接库)?] DLL是一个包含可由多个程序同时使用的代码和数据的库.例如:在Windows操作系统中,Comdlg32 DLL执行与对话框有关的常见函数.因此,每个程序都可以使用该 ...