NPOI操作Excel(三)--解析Excel

摘要:
通过前两篇文章的基础研究,我们对NPOI有了一定的了解。现在我们开始练习。我们将按以下格式分析Excel,并将其保存到仓库。要分析这样的Excel,我们需要将指标、账户和数据的数据分离到数据库中(Null)//读取第8行第一列中的数据{9ICellStyle=row.GetCell.CellStyle;10shortGroundColor=style.FillForegroundColor;11if(LeftDataColor!

通过前面两篇的基础学习,我们对NPOI有了一定了了解,下面就开始进入实战,解析下面格式的Excel(下面只是列举了几个例子),并保存入库

NPOI操作Excel(三)--解析Excel第1张NPOI操作Excel(三)--解析Excel第2张NPOI操作Excel(三)--解析Excel第3张

首先我们先分析一下,要解析这样的Excel,需要把指标【橘色背景和蓝色背景】(作为指标入库)、科目【棕色背景和黄色背景】(作为X轴入库)、数据【乳白色背景和白色背景】(作为Y轴入库)的数据分开入库。

第一张图我们得到的指标毫无疑问应该是第三行从第二列开始到最后一列的数据,而第二张图我们得到的指标应该是非金融企业部门-使用、非金融企业部门-来源、金融机构部门-使用、金融机构部门-来源,以此类推,我们要想取到这样的数据,首先需要把合并行的单元格填充、然后把合并列的数据合并,我们可以通过二维数组来实实现。

由于每个Excel的格式不一样,指标数据的行数,列数也不一样,所以我们要想把数据区分开只能通过背景颜色,把三部分是数据分开并放到三个二维数组里,然后解析入库,由于Excel的背景颜色存在不一样,所以不能写死,通过观察我们可以发现,每个Excel都是从指标行开始有背景颜色到数据行开始变背景颜色,这样我们就可以区分开来,到这里相信聪明的你已经知道怎么做了,下面我们就开始实现吧

1、获取Excel的扩展名并创建工作簿,如果是xls创建HSSFWorkbook工作簿,如果是xlxs创建XSSFWorkbook工作簿

 1     public static void ReadFromExcelFile(string filePath)
 2     {
 3         IWorkbook wk = null;
 4         string extension = System.IO.Path.GetExtension(filePath);//GetExtension获取Excel的扩展名
 5         try
 6         {
 7            FileStream fs = File.OpenRead(filePath);
 8            if (extension.Equals(".xls"))
 9            {                   
10                wk = new HSSFWorkbook(fs); //把xls文件中的数据写入wk中
11            }
12            else
13            {                    
14                wk = new XSSFWorkbook(fs);//把xlsx文件中的数据写入wk中
15            }
16            fs.Close();                
17            sheet = wk.GetSheetAt(0);//读取当前表数据   20            GetIndexRow();//获取【指标、科目、数据】的行数列数
21            ReadData();//读数据并保存到数组中
22            SaveData();//解析数组数据并保存入库
23         }
24         catch (Exception e)
25         {               
26            Console.WriteLine(e.Message); //只在Debug模式下才输出
27         }
28     }

2、获取指标从哪行开始

 1  for (int i = 0; i < sheet.LastRowNum; i++)//sheet.LastRowNum当前表的行数
 2  {
 3     IRow row = sheet.GetRow(i);  //读取当前行数据
 4     if (row != null)
 5     {
 6        if (row.GetCell(0) != null)  //读取该行的第1列数据
 7        {
 8          ICellStyle style = row.GetCell(0).CellStyle;//当前行第一列的样式
 9          row.GetCell(0).SetCellType(CellType.String);//把第一行第一列的值类型转换成string类型
10          short GroundColor = style.FillForegroundColor;//获取当前行第一列的背景色
11          if (i == 0)//若或i=0说明是第一行,没有背景色的
12          {
13             Title = row.GetCell(0).StringCellValue;//获取第一行第一列的值即标题的值
14             TitleColor = GroundColor;//第一行第一列背景色的值付给TitleColor
15             continue;
16          }
17          else//如果不是第一行
18          {
19             if (GroundColor == TitleColor)
20             {
21                if (row.GetCell(0).StringCellValue.Contains("单位"))
22                {
23                   IndexUnit = row.GetCell(0).StringCellValue.Replace("单位:", "").Replace("单位:", "");
24                   continue;
25                 }
26             }
27             else if (GroundColor != TitleColor && IndexColor == 0)//如果GroundColor不等于TitleColor说明改行是指标行
28             {
29                 IndexColor = GroundColor;// 把GroundColor的值赋值给IndexColor
30                 IndexStart = i;//记录改行,改行是指标行的起始行
31                 break;
32             }
33         }
34     }
35   }
36 }

 3、获取指标从哪行结束

 1 for (int i = IndexStart + 1; i < sheet.LastRowNum; i++)
 2 {
 3     IRow row = sheet.GetRow(i);  //读取当前行数据
 4      if (row != null)
 5      {
 6           if (row.GetCell(0) != null)  //读取该行的第1列数据
 7           {
 8                ICellStyle style = row.GetCell(0).CellStyle;
 9                short GroundColor = style.FillForegroundColor;
10                if (IndexColor != GroundColor)
11                {
12                      LeftDataColor = GroundColor;
13                      IndexEnd = i - 1;
14                      break;
15                }
16            }
17       }
18 }

4、获取数据从哪行开始到哪行结束

 1 for (int i = IndexEnd + 1; i < sheet.LastRowNum; i++)
 2 {
 3      DataRowStart = IndexEnd + 1;//数据开始行
 4      IRow row = sheet.GetRow(i);  //读取当前行数据
 5      if (row != null)
 6      {
 7           if (row.GetCell(0) != null)  //读取该行的第1列数据
 8           {
 9                 ICellStyle style = row.GetCell(0).CellStyle;
10                 short GroundColor = style.FillForegroundColor;
11                 if (LeftDataColor != GroundColor)
12                 {
13                       DataRowEnd = i - 1;//数据结束行
14                       break;
15                  }
16            }
17      }
18 }

5、获取科目【左侧】的列数

 1 if (sheet.GetRow(IndexEnd + 1) != null)
 2 {
 3        for (int i = 0; i < sheet.GetRow(IndexEnd + 1).LastCellNum; i++)
 4         {
 5               if (sheet.GetRow(IndexEnd + 1).GetCell(i) != null)
 6                {
 7                      ICellStyle style = sheet.GetRow(IndexEnd + 1).GetCell(i).CellStyle;
 8                       short GroundColor = style.FillForegroundColor;
 9                       sheet.GetRow(IndexEnd + 1).GetCell(i).SetCellType(CellType.String);
10                        if (GroundColor != LeftDataColor)
11                         {
12                             DataLeftCell = i;//科目的列数
13                             break;
14                         }
15                  } 
17            }
18 } 

6、把数据保存到数组中【指标数组】

 1 string[,] IndexArray = new string[IndexEnd-IndexStart+1, sheet.GetRow(0).LastCellNum - DataLeftCell];//指标
 2 
 3  4  //循环指标行
 5 for (int r = IndexStart; r <= IndexEnd; r++)
 6 {
 7    IRow row = sheet.GetRow(r);  //读取当前行数据
 8    if (row != null)
 9    {
10       for (int c = DataLeftCell; c <= row.LastCellNum - DataLeftCell; c++)
11       {
12           if (row.GetCell(c) != null)
13           {
14               row.GetCell(c).SetCellType(CellType.String);
15               #region 判断是否是合并单元格
16               if (string.IsNullOrEmpty(row.GetCell(c).StringCellValue))
17               {
18                    ICell cell = row.GetCell(c);
19                    Dimension dimension = new Dimension();
20                    if (IsMergedRegions.IsMergeCell(cell, out dimension))//如果是空判断是否是合并单元格
21                    {
22                         IndexArray[r - IndexStart, c- DataLeftCell] = dimension.DataCell.StringCellValue;//如果是取合并单元格的值
23                    }
24                    else
25                    {
26                         IndexArray[r - IndexStart, c- DataLeftCell] = row.GetCell(c).StringCellValue;//否则取改单元格本身的值
27                    }
28               }
29               else
30               {
31                    IndexArray[r - IndexStart, c- DataLeftCell] = row.GetCell(c).StringCellValue;
32               }
33               #endregion
34           }
35       }
36    }
37 }

7、把数据保存到数组中【科目数组】

 1 string[,]  LeftDataArray = new string[DataRowEnd-DataRowStart+1, DataLeftCell];//科目
 2  for (int r = DataRowStart; r <= DataRowEnd; r++)
 3             {
 4                 IRow row = sheet.GetRow(r);  //读取当前行数据
 5                 if (row != null)
 6                 {
 7                     for (int c = 0; c < DataLeftCell; c++)
 8                     {
 9                         if (row.GetCell(c) != null)
10                         {
11                             row.GetCell(c).SetCellType(CellType.String);
12 
13                             #region 判断是否是合并单元格
14                             if (string.IsNullOrEmpty(row.GetCell(c).StringCellValue))
15                             {
16                                 ICell cell = row.GetCell(c);
17                                 Dimension dimension = new Dimension();
18                                 if (IsMergedRegions.IsMergeCell(cell, out dimension))
19                                 {
20                                     LeftDataArray[r - DataRowStart, c] = dimension.DataCell.StringCellValue;
21                                 }
22                                 else
23                                 {
24                                     LeftDataArray[r - DataRowStart, c] = row.GetCell(c).StringCellValue;
25                                 }
26                             }
27                             else
28                             {
29                                 LeftDataArray[r - DataRowStart, c] = row.GetCell(c).StringCellValue;
30                             }
31                             #endregion
32                         }
33                     }
34                 }
35             }

8、把数据保存到数组中【数据数组】

 1 string[,]  RightDataArray= new string[DataRowEnd - DataRowStart + 1, sheet.GetRow(0).LastCellNum - DataLeftCell];//数据
 2  for (int r = DataRowStart; r <= DataRowEnd; r++)
 3             {
 4                 IRow row = sheet.GetRow(r);  //读取当前行数据
 5                 if (row != null)
 6                 {
 7                     for (int c = DataLeftCell; c < row.LastCellNum; c++)
 8                     {
 9                         if (row.GetCell(c) != null)
10                         {
11                             row.GetCell(c).SetCellType(CellType.String);
12                             RightDataArray[r - DataRowStart, c- DataLeftCell] = row.GetCell(c).StringCellValue;
13                         }
14                     }
15                 }
16             }

9、解析数组保存数据

 1  private static void SaveData()
 2  {
 3        //IndexModel im = new IndexModel();
 4        DataModel dm = new DataModel();
 5        for (int ic = 0; ic < sheet.GetRow(0).LastCellNum - DataLeftCell ; ic++)//循环指标列
 6        {
 7           dm.IndexName = null;
 8           dm.IndexCode = IndexCode++.ToString().PadLeft(4, '0');
 9           #region 获取指标名称
10           for (int ir = 0; ir < IndexEnd - IndexStart + 1; ir++)
11           {
12               if (IndexArray[ir, ic] != null)
13               {
14                    if (dm.IndexName == null)
15                    {
16                        dm.IndexName = IndexArray[ir, ic];                        
17                    }
18                    else
19                    {
20                        if (!dm.IndexName.Contains(IndexArray[ir, ic]))
21                        {
22                           dm.IndexName = dm.IndexName + "_" + IndexArray[ir, ic];//同一列字符串拼接
23                        }
24                    }
25                 }
26            }
27           #endregion
28           //循环得右侧数据
29           for (int rr = 0; rr < DataRowEnd - DataRowStart + 1; rr++)//循环右侧数据的行
30           {
31                 #region 右侧数据
32                 if (RightDataArray[rr, ic] != null)
33                 {
34                     dm.IndexYValue = RightDataArray[rr, ic];
35                 }
36                 #endregion
37                 dm.IndexXValue = null;
38                 //循环得左侧数据
39                 for (int lc = 0; lc < DataLeftCell; lc++)
40                 {
41                     if (LeftDataArray[rr, lc] !=null)
42                     {
43                          if (dm.IndexXValue == null)
44                          {
45                               dm.IndexXValue = LeftDataArray[rr, lc];
46                          }
47                          else
48                          {
49                               if (!dm.IndexXValue.Contains(LeftDataArray[rr, lc]))
50                               {
51                                   dm.IndexXValue = dm.IndexXValue + "_" + LeftDataArray[rr, lc];
52                               }
53                           }                              
54                       }
55                   }
56             Console.WriteLine($"指标名称:{dm.IndexName} 指标编码:{dm.IndexCode} IndexXValue:{dm.IndexXValue} IndexYValue:{dm.IndexYValue}");
57           }
58       }
59  }

 10、上面用到的方法IsMergeCell判断是否是合并单元格

 1 /// <summary>
 2 /// 判断指定单元格是否为合并单元格,并且输出该单元格的维度
 3 /// </summary>
 4 /// <param name="cell">单元格</param>
 5 /// <param name="dimension">单元格维度</param>
 6 /// <returns>返回是否为合并单元格的布尔(Boolean)值</returns>
 7 public static bool IsMergeCell(this ICell cell, out Dimension dimension)
 8 {
 9     return cell.Sheet.IsMergeCell(cell.RowIndex, cell.ColumnIndex, out dimension);
10 }

免责声明:文章转载自《NPOI操作Excel(三)--解析Excel》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇七 、linux正则表达式【android】读取/res/raw目录下的文件下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

MongoDB查询(数组、内嵌文档)

转自:http://blog.csdn.net/congcong68/article/details/46919227 一、简介 我们上一篇介绍了db.collection.find()可以实现根据条件查询和指定使用投影运算符返回的字段省略此参数返回匹配文档中的所有字段,我们今天介绍了对数组和内嵌文档的查询操作,尤其是对$elemMatch 同样可以用在...

C#窗体控件DataGridView常用设置

C#窗体控件DataGridView常用设置 在默认情况下,datagridview的显示效果: 1.禁用最后一行空白。   默认情况下,最后一行空白表示自动新增行,对于需要在控件中进行编辑,可以保留 dataGridView1.AllowUserToAddRows = false;   上述禁用,仅是将用户界面交互的自动新增行禁了,但还是可以通过代码:...

vue调用组件,组件回调给data中的数组赋值,报错Invalid prop type check failed for prop value. Expecte

报错信息: 代码信息:调用一个tree组件,选择一些信息 <componentsTree ref="typeTreeComponent"   @treeCheck="treeCheck"   :isClearAllChecked=true   :defaultProps="defaultProps"> </componentsTree&...

javascript 中数组的创建 添加 与将数组转换成字符串 页面三种提交请求的方式

创建js数组 var array=new Array(); Java中创建数组 private String[] array=new String[3]; 两个完全不同的,js中是可变长度的 添加内容 array.push(something); java中 array[0]="abc"; 数组转字符串 array.join(",") java中 St...

HDU 1541.Stars-一维树状数组(详解)

树状数组,学长很早之前讲过,最近才重视起来,enmmmm。。。 树状数组(Binary Indexed Tree(B.I.T), Fenwick Tree)是一个查询和修改复杂度都为log(n)的数据结构。主要用于查询任意两位之间的所有元素之和,但是每次只能修改一个元素的值;经过简单修改可以在log(n)的复杂度下进行范围修改,但是这时只能查询其中一个元素...

标准C程序设计七---32

Linux应用 编程深入 语言编程标准C程序设计七---经典C11程序设计以下内容为阅读:《标准C程序设计》(第7版) 作者:E. Balagurusamy(印), 李周芳译 清华大学出版社 2017.7《21天学通C语言》(第7版) 作者:Bradley Jones Peter Aitken Dean Miller(美), 姜佑译 人民邮电出版社 201...