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

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

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

......

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

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

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

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

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

第17行到33行

 1                    for (int j = headerRow.FirstCellNum; j < cellCount; j++)
 2                     {
 3                         if (!string.IsNullOrEmpty(headerRow.GetCell(j).StringCellValue.Trim()))
 4                         {
 5                             // 根据 键-值 是否已存在做不同处理
 6                             try
 7                             {
 8                                 string oldValue = dict[j];
 9                                 dict.Remove(j);
10                                 dict.Add(j, oldValue + headerRow.GetCell(j).StringCellValue.Trim());
11                             }
12                             catch (Exception)
13                             {
14                                 dict.Add(j, headerRow.GetCell(j).StringCellValue.Trim());
15                             }
16                         }
17                     }

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

0,地区

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

2,城镇人口人口数

3,比重(%)

4,乡村人口人口数

5,#比重(%)

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

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

注:

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

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

我们来总结一下:

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

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

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

......

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

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

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

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

3.左表头需要解析成

水位.潮汐性质

水位.历年最高潮位

水位.多年平均高潮位

...

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

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

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

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

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

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

 1                      if (value == "")
 2                     {
 3                         int firstRegionRow = 0;
 4                         if (_iCoreExcelAnalyzeService.IsMergedRegionCell(j, i, sheet, ref firstRegionRow))  //2、单元格为合并单元格且不在合并区域左上角
 5                         {
 6                             if (firstRegionRow >= lastHeaderRowIndex && i != firstRegionRow)//合并单元格  第一行无值为cell合并
 7                             {
 8                                 int resultIndex = firstRegionRow - lastHeaderRowIndex;
 9
10                                 var oldModel =
11                                     resultList.Select((p, d) => new { p, d })
12                                         .Where(p => p.d == resultIndex)
13                                         .Select(p => p.p).First();
14                                 var regionValue = oldModel.GetType().GetProperty(property).GetValue(oldModel, null);//获得合并单元格第一行数据
15                                 value = regionValue.ToString();
16                             }
17                         }
18                         else   //1、单元格空值
19                         {
20                             nullcount++;
21                         }
22                    }

注:

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

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

 1         // 判断单元格是否被合并
 2         public bool IsMergedRegionCell(int cellIndex, int rowIndex,ISheet sheet,ref int firstRegionRow)
 3         {
 4             bool isMerged = false;
 5             var regionLists = GetMergedCellRegion(sheet);
 6
 7             foreach (var cellRangeAddress in regionLists)
 8             {
 9                 for (int i = cellRangeAddress.FirstRow; i <= cellRangeAddress.LastRow; i++)
10                 {
11                     if (rowIndex == i)
12                     {
13                         for (int j = cellRangeAddress.FirstColumn; j <= cellRangeAddress.LastColumn; j++)
14                         {
15                             if (cellIndex == j)
16                             {
17                                 isMerged = true;
18                                 firstRegionRow = cellRangeAddress.FirstRow;
19                                 break;
20                             }
21                             else
22                             {
23                                 continue;
24                             }
25                         }
26                     }
27                     else
28                     {
29                         continue;
30                     }
31                 }
32             }
33
34             return isMerged;
35         }
36
37         // 获取合并区域信息
38         private List<CellRangeAddress> GetMergedCellRegion(ISheet sheet)
39         {
40             int mergedRegionCellCount = sheet.NumMergedRegions;
41             var returnList = new List<CellRangeAddress>();
42
43             for (int i = 0; i < mergedRegionCellCount; i++)
44             {
45                 returnList.Add(sheet.GetMergedRegion(i));
46             }
47
48             return returnList;
49         }

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

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

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

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

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

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

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

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

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

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

模板样式如下:

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

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

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

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

时间: 2024-10-01 04:48:50

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

C# 获取Excel中的合并单元格

C# 获取Excel中的合并单元格 我们在制作表格时,有时经常需要合并及取消合并一些单元格.在取消合并单元格时需要逐个查找及取消,比较麻烦.这里分享一个简单的方法来识别Excel中的合并单元格,识别这些合并单元格之后对这些单元格进行取消合并操作,还可以进行其他操作如设置格式等. 为了展示,这里我使用了一个模板Excel文件,它含有两个合并区域,如下图: 代码使用: 步骤1:实例化一个Workbook对象并加载Excel文档. Workbook workbook = new Workbook();

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

package com.email.jav; import java.io.File;import java.io.IOException;import java.net.URL; import jxl.Workbook;import jxl.format.UnderlineStyle;import jxl.write.Label;import jxl.write.WritableCellFormat;import jxl.write.WritableFont;import jxl.write.

NPOI_winfrom导出Excel表格(合并单元格、规定范围加外边框、存储路径弹框选择)

1.导出 1 private void btn_print_Click(object sender, EventArgs e) 2 { 3 DataTable dtNew = new DataTable(); 4 5 dtNew.Columns.Add(new DataColumn("commodity_name", typeof(object))); 6 dtNew.Columns.Add(new DataColumn("specifications", type

Excel用底纹突出单元格的数据给Excel单元格添加底纹效果

在表格中某些单元格的数据相对重要,需要突出显示,除了应用单元格样式外,还可以单独为单元格设置图案底纹,让其突出显示.(常见问题)excel中如何设置单元格的底纹怎样给EXCEL表格加底纹在EXCEL中对某个单元格设置25%灰色底纹怎么做Excel利用条件格式对包含公式的单元格突出显示如何更换Excel单元格中的底纹颜色 [解决方法,教程视频资料如下]资料来源:http://edu.51cto.com/course/15224.html 完整博客资料:http://blog.51cto.com/1

[办公应用]如何将excel合并单元格分拆后每个单元格上仍保留数据?

合并单元格虽然美观,但是无法进行排序.筛选等操作. 只有合并单元格拆分后才可以按常规进行统计.但是普通拆分后,excel仅保留合并单元格数据到区域左上角的单元格. 解决方案:选定多个合并单元格,应用本宏即可每个单元格均保留数据:Sub 拆分() Dim c As Range For Each c In ActiveSheet.UsedRange.Cells If c.MergeCells Then c.Select c.UnMerge Selection.Value = c.Value End

excel 合并 单元格内容

刚刚有人问怎么合并单元格内容,正好excel 我也不会,顺便查查记录一下 1.假设有两个单元格如下:           单元格1 单元格2           2. 在一个空白单元格输入 =( 这代表一个公式的开头. 如下:           =(             3. 在第二步输入完成后用鼠标点击单元格1, 这时候 = ( 后面会出现第一个单元格的坐标. 紧接着你输入 &" "& , 注意双引号中间有空格. 然后再点击单元格2, 这时候单元格2的坐标也输入

POI 实现合并单元格以及列自适应宽度

POI是apache提供的一个读写Excel文档的开源组件,在操作excel时常要合并单元格,合并单元格的方法是: sheet.addMergedRegion(new CellRangeAddress(1, 1, 0, 2)); 自适应列宽度: sheet.autoSizeColumn(1); sheet.autoSizeColumn(1, true); 这两种方式都是自适应列宽度,但是注意这个方法在后边的版本才提供,poi的版本不要太老. 注意:第一个方法在合并单元格的的单元格并不好使,必须用

在Asp.Net MVC中使用NPOI插件实现对Excel的操作(导入,导出,合并单元格,设置样式,输入公式)

前言 NPOI 是 POI 项目的.NET版本,它不使用 Office COM 组件,不需要安装 Microsoft Office,目前支持 Office 2003 和 2007 版本. 1.整个Excel表格叫做工作表:WorkBook(工作薄),包含的叫页(工作表):Sheet:行:Row:单元格Cell. 2.NPOI是POI的C#版本,NPOI的行和列的index都是从0开始 3.POI读取Excel有两种格式一个是HSSF,另一个是XSSF. HSSF和XSSF的区别如下: HSSF

NPOI之Excel——合并单元格、设置样式、输入公式

首先建立一个空白的工作簿用作测试,并在其中建立空白工作表,在表中建立空白行,在行中建立单元格,并填入内容: //建立空白工作簿 IWorkbook workbook = new HSSFWorkbook(); //在工作簿中:建立空白工作表 ISheet sheet = workbook.CreateSheet(); //在工作表中:建立行,参数为行号,从0计 IRow row = sheet.CreateRow(0); //在行中:建立单元格,参数为列号,从0计 ICell cell = ro