通过前面两篇的基础学习,我们对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的更多相关文章

  1. Java上传下载excel、解析Excel、生成Excel

    在软件开发过程中难免需要批量上传与下载,生成报表保存也是常有之事,最近集团门户开发用到了Excel模版下载,Excel生成,圆满完成,对这一知识点进行整理,资源共享,有不足之处还望批评指正,文章结尾提 ...

  2. oracle xmltype导入并解析Excel数据 (三)解析Excel数据

    包声明 create or replace package PKG_EXCEL_UTILS is -- Author: zkongbai-- Create at: 2016-07-06-- Actio ...

  3. 使用POI做的一个生成Excel的工具类。包含了导出Excel和解析Excel方法

    PoiExcelUtils.java /** * */ package com.common.office; import java.io.File; import java.io.FileInput ...

  4. POI使用:用poi接口不区分xls/xlsx格式解析Excel文档(41种日期格式解析方法,5种公式结果类型解析方法,3种常用数值类型精度控制办法)

    一.使用poi解析excel文档 注:全部采用poi接口进行解析,不需要区分xls.xlsx格式,不需要判断文档类型. poi中的日期格式判断仅支持欧美日期习惯,对国内的日期格式并不支持判断,怎么办? ...

  5. Java解析Excel之应用Reflection等技术实现动态读取

    目录树 背景 技术选型 问题分析 技术要点及难点分析 源码分析 测试用例 背景 Tip:因为产品提的需求我都开发完了,进行了项目提测:前天老大走过来说:你用spring-boot开发一个解析Excel ...

  6. NPOI操作EXCEL(三)——反射机制进行excel表格数据的解析

    我们先来回忆回忆上篇文章讲到的通过xml配置文件实现excel批量模板解析的整体思路: 1.对每个excel模板制定xml配置规则集,实现xml配置文件的解析服务 2.为每个excel模板制定DTO, ...

  7. NPOI操作EXCEL(六)——矩阵类表头EXCEL模板的解析

    哈哈~~~很高兴还活着.总算加班加点的把最后一类EXCEL模板的解析做完了... 前面几篇文章介绍了博主最近项目中对于复杂excel表头的解析,写得不好,感谢园友们的支持~~~ 今天再简单讲诉一下另一 ...

  8. NPOI操作EXCEL(二)——大量不同模板时设计方式

    上一篇文章介绍了一些NPOI的基础接口,我们现在就来看看具体怎么用NPOI来解析一个EXCEL. 博主现在有这么一堆excel需要解析数据入库: 当然这只是员工的简要模板,还有很多其他的模板.我们可以 ...

  9. NPOI操作EXCEL(四)——反射机制批量导出excel文件

    前面我们已经实现了反射机制进行excel表格数据的解析,既然有上传就得有下载,我们再来写一个通用的导出方法,利用反射机制实现对系统所有数据列表的筛选结果导出excel功能. 我们来构想一下这样一个画面 ...

随机推荐

  1. Python笔记 【无序】 【五】

    描述符 将某种特殊类型的类[只要实现了以下或其中一个]的实例指派给另一个类的属性 1.__get__(self,instance,owner)//访问属性,返回属性的值 2.__set__(self, ...

  2. Codeforces 408D Long Path (DP)

    题目: One day, little Vasya found himself in a maze consisting of (n + 1) rooms, numbered from 1 to (n ...

  3. configparser模块--配置文件

    该模块适用于配置文件的格式与windows ini文件类似,可以包含一个或多个节(section),每个节可以有多个参数(键=值). 创建文件 import configparser config = ...

  4. hibernate学习笔记第七天:二级缓存和session管理

    二级缓存配置 1.导入ehcache对应的三个jar包 ehcache/*.jar 2.配置hibernate使用二级缓存 2.1设置当前环境开始二级缓存的使用 <property name=& ...

  5. iperf 2.05版本升级到2.0.9

    将openwrt  trunk 分支上iperf 2.0.9移植到 bb版本上时,编译遇到如下问题: make[6]: Entering directory '/home/hbg/bb/build_d ...

  6. centos 6 不能上网

    今天安装了一个CentOS 6,默认安装的Centos不不能上网? 解决办法:Centos 默认的链接网路的方式为:NAT方式.如果无法上网,设置Network Connection 的方式为Brid ...

  7. linux shell中 if else以及大于、小于、等于逻辑表达式

    在linux shell编程中,大多数情况下,可以使用测试命令来对条件进行测试,这里简单的介绍下,方便需要的朋友 比如比较字符串.判断文件是否存在及是否可读等,通常用"[]"来表示 ...

  8. Centos7升级gcc版本方法之一使用scl软件集

    Centos7 gcc版本默认4.8.3,Red Hat 为了软件的稳定和版本支持,yum 上版本也是4.8.3,所以无法使用yum进行软件更新,所以使用scl. scl软件集(Software Co ...

  9. Go数组和切片定义和初始化

    1 前言 切片是动态数组,数组数组是按值赋值,切片是按地址赋值(引用) 2 代码 2.1 数组初始化 func basic_array(){ //var arr2 = [3]int{2,4,6} // ...

  10. better-scroll项目中遇到的问题

    1.在项目中发现个问题,用better-scroll实现的轮播图和页面滚动条俩个效果一起出现的时候,当鼠标或手指放在轮播图位置的时候,上下滚动的时候,页面滚动条不动 发现最新的版本就会出这个问题,就是 ...