最近处理了一个需求,把一个Excel文件的数据读取出来做分析,但这个Excel文件是多表头的。经过开发实现了这个功能,和没有表头的读取操作还是有区别的。下面就分享下这个功能的开发思路,基于Aspose.Cells组件
先制作一个测试文件,表头如下图所示
导入多表头Excel文档
导入的方法,要传入很重要的一个参数,就是有几级表头(上图有3级)。只要这个确定了,才能实现导入多表头的功能。思路也简单,就是先遍历出哪些单元格是合并的。读取表头时,如果是合并的就找上一级的单元格,然后所有层的表头再拼到一起
调用的代码
//(ok)多表头的Excel文件,导入 string xlsFile1 = SystemHelper.GetSystemPath(SystemFolderName.Desktop) + @"\" + @"tempfile\多表头测试.xlsx"; DataTable dtData = NS.Utility.Office.ExcelUtil.ImportDataTableByMultiTitle(xlsFile1, "Sheet1", 2);
实现的完整代码
/// <summary> /// 若是多表头,则需拼接父层级的标题 /// </summary> /// <param name="workSheet"></param> /// <param name="titleLevel"></param> /// <param name="columnIndex"></param> /// <param name="dicMerge"></param> /// <returns></returns> private static string GetColumnNameByMultiTitle(Worksheet workSheet, int titleLevel, int columnIndex,Dictionary<string,List<string>>dicMerge) { string columnName = null; if (titleLevel == 0) { columnName = workSheet.Cells[titleLevel, columnIndex].Value.ToString(); } else { List<string> listP = new List<string>(); int level = titleLevel; while (level >= 0) { Aspose.Cells.Cell cell = workSheet.Cells[level, columnIndex]; object cellValue = cell.Value; if (StringHelper.ObjectIsNullOrEmpty(cellValue)) { //若为空,判断是不是合并单元格 if (cell.IsMerged) { string search = string.Format("{0}-{1}", cell.Row, cell.Column); foreach(string key in dicMerge.Keys) { List<string> listValue = dicMerge[key]; if (listValue.Contains(search)) { //若找到,则使用Key的单元格值 string[] arrayKey = key.Split('-'); int mergeRow = Convert.ToInt32(arrayKey[0]); int mergeCol = Convert.ToInt32(arrayKey[1]); cellValue = workSheet.Cells[mergeRow, mergeCol].Value; break; } } } } if (!listP.Contains(cellValue.ToString())) listP.Add(cellValue.ToString()); level--; } //需要反转 listP.Reverse(); //再拼接 columnName = string.Join("-", listP); } return columnName; } /// <summary> /// 导入Excel文件数据,支持多表头 /// </summary> /// <param name="xlsFile"></param> /// <param name="sheetName"></param> /// <param name="titleLevel">表头最明细的行号。注:行号从0开始</param> /// <returns></returns> public static DataTable ImportDataTableByMultiTitle(string xlsFile, string sheetName = "Sheet1", int titleLevel = 0) { //参数检测 if (!System.IO.File.Exists(xlsFile)) { throw new Exception("Excel文件未找到"); } if (string.IsNullOrEmpty(sheetName)) { sheetName = "Sheet1"; } Workbook workBook = new Workbook(xlsFile); if (workBook == null || workBook.Worksheets == null || workBook.Worksheets.Count <= 0) { throw new Exception("获取Sheet页集合失败"); } Worksheet workSheet = workBook.Worksheets[sheetName]; if (workSheet == null) { throw new Exception("获取Sheet名为[" + sheetName + "]的页失败"); } DataTable dataTable = null; //获取栏目个数,若不存在,则继续循环.注:不知为何,需要加1 int columnCount = workSheet.Cells.MaxColumn + 1; if (columnCount <= 0) return dataTable; dataTable = new DataTable(); //先将合并的单元格找出来,用于拼接标题 List<string> listP2 = new List<string>(); Dictionary<string, List<string>> dicMerge = new Dictionary<string, List<string>>(); foreach (var mergeCell in workSheet.Cells.MergedCells) { //Aspose.Cells.CellArea(A1:A3)[0,0,2,0] string mergeCell2 = mergeCell.ToString(); listP2.Add(mergeCell2); string temp1 = StringHelper.Between(mergeCell2, "[", "]"); string[] array2 = temp1.Split(','); int startRow = Convert.ToInt32(array2[0]); int startCol = Convert.ToInt32(array2[1]); int endRow = Convert.ToInt32(array2[2]); int endCol = Convert.ToInt32(array2[3]); string key = string.Format("{0}-{1}", startRow, startCol); List<string> listValue = new List<string>(); for(int i = 0; i <= endRow - startRow; i++) { for (int j = 0; j <= endCol - startCol; j++) { string temp = string.Format("{0}-{1}", startRow + i, startCol + j); listValue.Add(temp); } } dicMerge.Add(key, listValue); } //生成DataTable对象表结构,即获取第一行单元格值 List<string> listCol = new List<string>(); for (int j = 0; j < columnCount; j++) { string columnName = ExcelUtil.GetColumnNameByMultiTitle(workSheet, titleLevel, j, dicMerge); if (StringHelper.ObjectIsNullOrEmpty(columnName)) { columnName = "ColumnName" + (j + 1).ToString(); } dataTable.Columns.Add(columnName.ToString(), typeof(string)); listCol.Add(columnName.ToString()); } string cols = string.Join(Environment.NewLine, listCol); //填充数据 int rowCount = workSheet.Cells.MaxDataRow + 1 - titleLevel; if (rowCount > 0) { for (int m = 1; m < rowCount; m++) { DataRow dataRow = dataTable.NewRow(); for (int n = 0; n < columnCount; n++) { object cellValue = workSheet.Cells[titleLevel + m, n].Value; dataRow[n] = cellValue; } dataTable.Rows.Add(dataRow); } } return dataTable; }
导出多表头Excel文档
目前仅支持2层多表头,直接在DataColumn.Caption中体现。有兴趣的小伙伴可以扩展支持3层或无限层。
调用的代码
//(ok)多表头的Excel文件,导出,方法3 DataTable dtData = new DataTable(); dtData.Columns.Add("ShopCode", typeof(string)); dtData.Columns.Add("SaleUser", typeof(string)); dtData.Columns.Add("AliSaleCount", typeof(int)); dtData.Columns.Add("AliSaleAmount", typeof(decimal)); dtData.Columns.Add("SheSaleCount", typeof(int)); dtData.Columns.Add("SheSaleAmount", typeof(decimal)); dtData.Columns.Add("AmaSaleCount", typeof(int)); dtData.Columns.Add("AmaSaleAmount", typeof(decimal)); dtData.Columns.Add("SumSaleCount", typeof(int)); dtData.Columns.Add("SumSaleAmount", typeof(decimal)); dtData.Columns.Add("Remarks", typeof(string)); dtData.Columns[0].Caption = "店铺"; dtData.Columns[1].Caption = "运营人"; dtData.Columns[2].Caption = "速卖通-订单量"; dtData.Columns[3].Caption = "速卖通-销售额"; dtData.Columns[4].Caption = "Shopee-订单量"; dtData.Columns[5].Caption = "Shopee-销售额"; dtData.Columns[6].Caption = "亚马逊-订单量"; dtData.Columns[7].Caption = "亚马逊-销售额"; dtData.Columns[8].Caption = "合计-订单量"; dtData.Columns[9].Caption = "合计-销售额"; dtData.Columns[10].Caption = "备注"; string xlsFile2 = SystemHelper.GetSystemPath(SystemFolderName.Desktop) + @"\" + @"tempfile\多表头测试-export2.xlsx"; if (System.IO.File.Exists(xlsFile2)) System.IO.File.Delete(xlsFile2); NS.Utility.Office.ExcelUtil.ExportDataTableByMultiTitle(dtData, xlsFile2);
实现的完整代码
/// <summary> /// 导出数据至Excel文件,支持多表头 /// 目前仅支持2级 /// </summary> /// <param name="dtData"></param> /// <param name="xlsFile"></param> public static void ExportDataTableByMultiTitle(DataTable dtData, string xlsFile) { int rowLevel = 0; List<ExportMultiTitleColumnModel> listCol = new List<ExportMultiTitleColumnModel>(); //生成列信息,包括是否合并等 foreach (DataColumn col in dtData.Columns) { string caption = col.Caption; if (caption.IndexOf('-') >= 0) { //表示多表头 string[] array1 = caption.Split('-'); rowLevel = Math.Max(rowLevel, array1.Length); listCol.Add(new ExportMultiTitleColumnModel() { Caption = caption, ColumnTitle = array1[1], ParentColumn = array1[0], }); } else { listCol.Add(new ExportMultiTitleColumnModel() { Caption = caption, ColumnTitle = caption, }); } } var g1All = listCol.Where(c => !string.IsNullOrEmpty(c.ParentColumn)).GroupBy(c => new { c.ParentColumn }); foreach (var g1 in g1All) { string parent = g1.Key.ParentColumn; var list1 = listCol.Where(c => c.ParentColumn == parent).ToList(); //计算有几列? listCol.Add(new ExportMultiTitleColumnModel() { Caption = parent, ColumnTitle = parent, ColSpan = list1.Count, }); } //用于操作的单元格集合 Workbook workBook = new Workbook(); workBook.Worksheets.Clear(); string tableName = dtData.TableName; if (string.IsNullOrEmpty(tableName)) tableName = "Sheet1"; workBook.Worksheets.Add(tableName); Worksheet workSheet = workBook.Worksheets[0]; Aspose.Cells.Cells cells = workSheet.Cells; //记录已添加的父级标题 List<string> listParentCol = new List<string>(); for(int i = 0; i < dtData.Columns.Count; i++) { DataColumn dc = dtData.Columns[i]; string caption = dc.Caption; var modelCol = listCol.FirstOrDefault(c => c.Caption == caption); if (rowLevel > 0) { //说明存在多表头 if (string.IsNullOrEmpty(modelCol.ParentColumn)) { cells[0, i].PutValue(modelCol.ColumnTitle); //合并单元格 cells.Merge(0, i, rowLevel, 1); } else { if (!listParentCol.Contains(modelCol.ParentColumn)) { var modelParentCol = listCol.FirstOrDefault(c => c.Caption == modelCol.ParentColumn); cells[0, i].PutValue(modelCol.ParentColumn); cells.Merge(0, i, 1, modelParentCol.ColSpan); listParentCol.Add(modelCol.ParentColumn); } cells[1, i].PutValue(modelCol.ColumnTitle); } } else { cells[0, i].PutValue(modelCol.ColumnTitle); } } workBook.Save(xlsFile); }
相关阅读
文章评论