NPOI操作EXCEL(五)——含合并单元格复杂表头的EXCEL解析

时间:2022-01-28 12:54:17

我们在第三篇文章中谈到了那些非常反人类的excel模板,博主为了养家糊口,也玩命做出了相应的解析方法...

我们先来看看第一类复杂表头:

NPOI操作EXCEL(五)——含合并单元格复杂表头的EXCEL解析

NPOI操作EXCEL(五)——含合并单元格复杂表头的EXCEL解析

NPOI操作EXCEL(五)——含合并单元格复杂表头的EXCEL解析

......

博主称这类excel模板为略复杂表头模板(蓝色部分为表头部分,蓝色前面几行是博主项目的基础样式,称为元数据),这类excel的表头多为2-3行,甚至于5/6行 ,具有合并层级关系,看似复杂,但只需要在我们以前的基础上稍微做一下重构就可以完美实现解析。

我们以各地区户籍人口城乡构成表头为例:

NPOI操作EXCEL(五)——含合并单元格复杂表头的EXCEL解析

其实,只要我们能准确解析这类表头所表达的意思,就能复用以前的代码做解析工作

也就是说,重点在于表头解析方法GetExcelHeaders(),

我们返回看第三篇文章http://www.cnblogs.com/csqb-511612371/p/4891492.html中这个方法的代码:

第17行到33行

                    for (int j = headerRow.FirstCellNum; j < cellCount; j++)
{
if (!string.IsNullOrEmpty(headerRow.GetCell(j).StringCellValue.Trim()))
{
// 根据 键-值 是否已存在做不同处理
try
{
string oldValue = dict[j];
dict.Remove(j);
dict.Add(j, oldValue + headerRow.GetCell(j).StringCellValue.Trim());
}
catch (Exception)
{
dict.Add(j, headerRow.GetCell(j).StringCellValue.Trim());
}
}
}

我们在这儿做了一个列的循环,对表头所在行每一列做了一个值合并,那么我们可以预料:这个表头解析出来的结果:

0,地区

1,总人口(年末)(万人)

2,城镇人口人口数

3,比重(%)

4,乡村人口人口数

5,#比重(%)

那么我们的xml配置文件就该写成这样:

 <?xml version="1.0" encoding="utf-8" ?>
<module>
<add firstHeaderRow="" lastHeaderRow=""/>
<add headerText="年份" propertyName="Year" dataType="System.Int32"/>
<add headerText="总人口(年末)(万人)" propertyName="TotalAmount" dataType="System.double"/>
<add headerText="城镇人口人口数" propertyName="UrbanPermanentPopulation" dataType="System.double"/>
<add headerText="比重(%)" propertyName="UrbanPermanentPopulationShare" dataType="System.double"/>
<add headerText="乡村人口人口数" propertyName="RuralPermanentPopulation" dataType="System.double"/>
<add headerText="#比重(%)" propertyName="RuralPermanentPopulationShare" dataType="System.double"/>
</module>

注:

1.第三行:5-7代表模板表头所在位置

OK,我们这样做就解析出了这个含有合并单元格的表头,那么接下来的所有流程就和简单表头一样了。

我们来总结一下:

1.修改配置文件xml,按我们的解析规则做映射配置

2.重构表头解析方法,按我们的配置解析表头数据

我们再来看看更复杂的表头模板:

NPOI操作EXCEL(五)——含合并单元格复杂表头的EXCEL解析

NPOI操作EXCEL(五)——含合并单元格复杂表头的EXCEL解析

......

这类模板除了表头外,还含有左表头。当然左表头也是需要存入数据库的,只是需要我们能准确解析到合并单元格所表达内容,方便导出是还原excel数据样式

以第一个excel水文特征值为例:

1.表头按合并表头做xml配置

2.左表头深色部分为合并区域,浅色部分为弱区域,可扩充

3.左表头需要解析成

水位.潮汐性质

水位.历年最高潮位

水位.多年平均高潮位

...

好吧,那么我们这次需要重构的就是解析数据的方法GetExcelDatas,我们看看第三篇文章:

http://www.cnblogs.com/csqb-511612371/p/4891492.html中的代码

我们看到第42-45行,对空值只做了简单处理,那么我们先来普及一下NPOI遇到合并单元格怎么取值?

NPOI只能取到合并单元格最左上角单元格的值,其它单元格均为空值。那么既然我们的数据中含有左表头含有合并单元格,这个空值就需要做一个复杂判断了

我们先来捋一捋思路:如果取值时遇到空值,可能是单元格本就是空值,也可能是该单元格是合并单元格,切不在合并坐标左上角...

OK,我们来看重构后的这一段代码

                      if (value == "")
{
int firstRegionRow = ;
if (_iCoreExcelAnalyzeService.IsMergedRegionCell(j, i, sheet, ref firstRegionRow)) //2、单元格为合并单元格且不在合并区域左上角
{
if (firstRegionRow >= lastHeaderRowIndex && i != firstRegionRow)//合并单元格 第一行无值为cell合并
{
int resultIndex = firstRegionRow - lastHeaderRowIndex; var oldModel =
resultList.Select((p, d) => new { p, d })
.Where(p => p.d == resultIndex)
.Select(p => p.p).First();
var regionValue = oldModel.GetType().GetProperty(property).GetValue(oldModel, null);//获得合并单元格第一行数据
value = regionValue.ToString();
}
}
else //1、单元格空值
{
nullcount++;
}
}

注:

1.第4行涉及方法IsMergedRegionCell()是用来判断当前空值单元格是否是合并单元格,并返回合并单元格起始行

我们查阅NPOI接口得知,目前并不支持直接判断,只有通过自己的逻辑去判断是否是合并单元格(不知道是否是博主未查到准确的API,如有该API,请指出...)

         // 判断单元格是否被合并
public bool IsMergedRegionCell(int cellIndex, int rowIndex,ISheet sheet,ref int firstRegionRow)
{
bool isMerged = false;
var regionLists = GetMergedCellRegion(sheet); foreach (var cellRangeAddress in regionLists)
{
for (int i = cellRangeAddress.FirstRow; i <= cellRangeAddress.LastRow; i++)
{
if (rowIndex == i)
{
for (int j = cellRangeAddress.FirstColumn; j <= cellRangeAddress.LastColumn; j++)
{
if (cellIndex == j)
{
isMerged = true;
firstRegionRow = cellRangeAddress.FirstRow;
break;
}
else
{
continue;
}
}
}
else
{
continue;
}
}
} return isMerged;
} // 获取合并区域信息
private List<CellRangeAddress> GetMergedCellRegion(ISheet sheet)
{
int mergedRegionCellCount = sheet.NumMergedRegions;
var returnList = new List<CellRangeAddress>(); for (int i = ; i < mergedRegionCellCount; i++)
{
returnList.Add(sheet.GetMergedRegion(i));
} return returnList;
}

博主只查阅到NPOI有sheet所有合并区域属性,以及获取某合并区域合并坐标方法...故做了此方法来做判断

2.第6-16行则是在获取合并单元格值,具体思路是:

合并坐标起始行不等于当前行,若等则代表有列合并(已经是空值),而我们暂不对列合并做值的特殊处理

resultIndex是计算该合并单元格值已被读取到DTO中的索引

oldModel是获得含有该合并单元格值的数据对象

这样,我们就成功的读取到了左合并单元格的数据,在入库时稍作处理即可得到我们想要的“水位.潮汐性质”数据字段。

至此,我们已经完成了绝大部分excel表格模板的解析工作。

上述代码如有任何不对之处,欢迎指出,一定虚心请教~~~

不过,博主的甲方特别难缠,最近又给了一个矩阵模板excel,让解析入库,还说有同样类型的模板很多个....

意思很明显,这尼玛又得加班加点的搞了...

模板样式如下:

NPOI操作EXCEL(五)——含合并单元格复杂表头的EXCEL解析

出发城市、到达城市内容、个数不定,意思就是连表头内容都是不确定的....

还要求数据进库后,再能把筛选出来的数据按原模板顺序导出....

好吧,吐血中~~~如果博主下周还活着,请关注下一篇文章查看解决方案(为什么是下周呢?因为尼玛这周末是最后期限...)

原创文章,代码都是从自己项目里贴出来的。转载请注明出处哦,亲~~~

NPOI操作EXCEL(五)——含合并单元格复杂表头的EXCEL解析的更多相关文章

  1. C&num; 获取Excel中的合并单元格

    C# 获取Excel中的合并单元格 我们在制作表格时,有时经常需要合并及取消合并一些单元格.在取消合并单元格时需要逐个查找及取消,比较麻烦.这里分享一个简单的方法来识别Excel中的合并单元格,识别这 ...

  2. C&num; 如何使用NPOI操作Excel以及读取合并单元格等

    C#操作Excel方法有很多,以前用的需要电脑安装office才能用,但因为版权问题公司不允许安装office.所以改用NPOI进行Excel操作,基本上一些简单的Excel操作都没有问题,读写合并单 ...

  3. 【转载】jxl操作excel 字体 背景色 合并单元格 列宽等 &period;

    package com.email.jav; import java.io.File;import java.io.IOException;import java.net.URL; import jx ...

  4. EXCEL合并单元格快捷键暨WORD&plus;EXCEL自定义快捷键

    最近在写测试用例时,用到合并单元格,只能点,没有快捷键,觉得很蛋疼,上网找了一下,没有直接设置其对应快捷键的方法,但有种曲线救国的方法: 一.右击功能区,选择‘自定义快速访问工具栏’   二.可以在这 ...

  5. EXCEL自动撤销合并单元格并填充相应内容&lpar;转帖&rpar;

    若EXCEL工作表有很多合并的单元格,要将所有合并的单元格撤销,并填充撤销合并前显示的内容,这是一项很繁琐且容易出错的工作.但可通过宏程序可轻松准确地搞定,方法如下: 一.实现该功能的Excel宏程序 ...

  6. Java导出Excel表,POI 实现合并单元格以及列自适应宽度(转载)

    POI是apache提供的一个读写Excel文档的开源组件,在操作excel时常要合并单元格,合并单元格的方法是: sheet.addMergedRegion(new CellRangeAddress ...

  7. &lbrack;办公应用&rsqb;如何将excel合并单元格分拆后每个单元格上仍保留数据?

    合并单元格虽然美观,但是无法进行排序.筛选等操作. 只有合并单元格拆分后才可以按常规进行统计.但是普通拆分后,excel仅保留合并单元格数据到区域左上角的单元格. 解决方案:选定多个合并单元格,应用本 ...

  8. WPF 导出Excel(合并单元格)

    WPF 导出Excel(合并单元格) DataTable 导出Excel(导出想要的列,不想要的去掉) ,B1,B2,B3,B4,B5} MisroSoft.Office.Interop.Excel. ...

  9. excel 合并 单元格内容

    刚刚有人问怎么合并单元格内容,正好excel 我也不会,顺便查查记录一下 1.假设有两个单元格如下:           单元格1 单元格2           2. 在一个空白单元格输入 =( 这代 ...

随机推荐

  1. git学习(二)&colon;查看状态和具体改动

    查看仓库状态 git status 可以查看有没有文件改动 查看改动情况 git diff readme.txt 以readme.txt为例,可以看readme.txt的改动情况,但对于二进制文件来说 ...

  2. &lbrack;C&num;&rsqb; 语法之Attribute

    在c#中,定义类的成员,可以定义Property称为属性.Attribute就称为特性. 在FCL中,有内置的Attribute.如: Condition[Attribute]:在什么条件可以调用.( ...

  3. Hive基础之Hive开启查询列名及行转列显示

    Hive默认情况下查询结果里面是只显示值: hive> select * from click_log; OK ad_101 :: ad_102 :: ad_103 :: ad_104 :: a ...

  4. Memcached学习&lpar;一&rpar;

    1.Memcached是什么? 引用*上得简介,Memcached 是一套分布式的高速缓存系统,由LiveJournal的Brad Fitzpatrick开发,目前已被诸如Facebook等许多 ...

  5. Oracle客户端PL&lowbar;SQL的安装

    Oracle数据库的操作大多还是在客户端完成的,因此在众多的客户端软件中我选择了PL_SQL,一下谈谈PL_SQL的基本安装和操作,以及在操作中碰到的一些问题: 1. 首先下载PL_SQL客户端软件, ...

  6. redis中各种数据类型对应的jedis操作命令

    redis中各种数据类型对应的jedis操作命令 一.常用数据类型简介: redis常用五种数据类型:string,hash,list,set,zset(sorted set). 1.String类型 ...

  7. trap-接收信号&lowbar;采取行动

    trap命令用于指定在接收到信号后将要采取的动作,常见的用途是在脚本程序被中断时完成清理工作. kill和trap等都可以看到信号编号及其关联的名称. "信号"是指那些被异步发送到 ...

  8. 快速搭建CentOS&plus;ASP&period;NET Core环境支持WebSocket

    环境:CentOS 7.x,.net core 2 以下.net core 2安装操作为官方方法.如果你使用Docker,那么更简单了,只需要docker pull microsoft/dotnet就 ...

  9. Hessian 源码简单分析

    Hessian 是一个rpc框架, 我们需要先写一个服务端, 然后在客户端远程的调用它即可. 服务端: 服务端通常和spring 做集成. 首先写一个接口: public interface Hell ...

  10. 苹果开发小记(一):NSString 的比较用法

    转自:http://blog.sina.com.cn/s/blog_897dd7be0100teh6.html 做了几个月的苹果,很多的思想方法都可以遵循一定规律来做的.NSString 比较字符串, ...