通過前面兩篇的基礎學習,我們對NPOI有了一定了了解,下面就開始進入實戰,解析下面格式的Excel(下面只是列舉了幾個例子),並保存入庫
首先我們先分析一下,要解析這樣的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 }