【.net core】【NPOI】读取表格信息(处理合并行表格数据)
NPOI版本:2.7.4
帮助类:
/// <summary>
/// NPOI帮助类
/// </summary>
public static class NPOIHelper
{/// <summary>/// 拆分合并单元格并填充数据到每个子单元格中/// </summary>/// <param name="sheet"></param>public static void UnmergeAndFill(ISheet sheet){// 注意:需要倒序遍历,避免拆分后索引变化for (int i = sheet.MergedRegions.Count - 1; i >= 0; i--){CellRangeAddress region = sheet.MergedRegions[i];// 获取合并区域左上角单元格的值string value = GetMergedCellValue(sheet, region.FirstRow, region.FirstColumn);// 拆分合并区域sheet.RemoveMergedRegion(i);// 给拆分后的每个单元格填充值for (int row = region.FirstRow; row <= region.LastRow; row++){IRow currentRow = sheet.GetRow(row) ?? sheet.CreateRow(row);for (int col = region.FirstColumn; col <= region.LastColumn; col++){ICell currentCell = currentRow.GetCell(col) ?? currentRow.CreateCell(col);currentCell.SetCellValue(value);}}}}public static string GetMergedCellValue(ISheet sheet, int rowIndex, int cellIndex){// 遍历所有合并区域foreach (var region in sheet.MergedRegions){// 判断当前单元格是否在合并区域内if (region.IsInRange(rowIndex, cellIndex)){// 返回合并区域左上角单元格的值IRow row = sheet.GetRow(region.FirstRow);ICell cell = row?.GetCell(region.FirstColumn);return GetCellValue(cell);}}// 非合并单元格直接返回自身值IRow currentRow = sheet.GetRow(rowIndex);ICell currentCell = currentRow?.GetCell(cellIndex);return GetCellValue(currentCell);}// 辅助方法:获取单元格实际值(处理不同数据类型)private static string GetCellValue(ICell cell){if (cell == null) return "";switch (cell.CellType){case CellType.String:return cell.StringCellValue;case CellType.Numeric:if (DateUtil.IsCellDateFormatted(cell))return cell.DateCellValue == null ? "" : ((DateTime)cell.DateCellValue).ToString("yyyy-MM-dd");elsereturn cell.NumericCellValue.ToString();case CellType.Boolean:return cell.BooleanCellValue.ToString();default:return "";}}
}
调用:
/// <summary>
/// 读取Excel文件并将第二行作为DataTable的表头
/// </summary>
/// <param name="filePath">Excel文件路径</param>
/// <param name="sheetIndex">工作表索引</param>
/// <returns>包含数据的DataTable</returns>
private DataTable ReadExcelWithSecondRowAsHeader(string filePath, int sheetIndex = 0)
{DataTable dataTable = new DataTable();using (FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read)){IWorkbook workbook;// 根据文件格式创建对应的工作簿if (filePath.EndsWith(".xlsx", StringComparison.OrdinalIgnoreCase)){workbook = new XSSFWorkbook(fileStream);}else if (filePath.EndsWith(".xls", StringComparison.OrdinalIgnoreCase)){workbook = new HSSFWorkbook(fileStream);}else{throw new ArgumentException("不支持的文件格式");}// 获取指定的工作表ISheet sheet = workbook.GetSheetAt(sheetIndex);if (sheet == null){return dataTable;}//调用处理合并单元格数据方法NPOIHelper.UnmergeAndFill(sheet);// 获取总行数int rowCount = sheet.LastRowNum;if (rowCount < 1) // 至少需要有两行数据(表头行+至少一行数据){return dataTable;}// 第二行作为表头(索引从0开始,所以是rowIndex=1)IRow headerRow = sheet.GetRow(1);if (headerRow == null){return dataTable;}// 根据表头行创建DataTable的列int cellCount = headerRow.LastCellNum;for (int i = 0; i < cellCount; i++){ICell cell = headerRow.GetCell(i);string columnName = cell?.ToString() ?? $"Column_{i}";// 如果列名重复,添加序号区分if (dataTable.Columns.Contains(columnName)){columnName = $"{columnName}_{i}";}dataTable.Columns.Add(columnName);}// 从第三行开始读取数据(rowIndex从2开始)for (int i = 2; i <= rowCount; i++){IRow dataRow = sheet.GetRow(i);if (dataRow == null){continue;}DataRow row = dataTable.NewRow();// 填充行数据for (int j = 0; j < cellCount; j++){ICell cell = dataRow.GetCell(j);if (cell != null){// 根据单元格类型获取对应的值switch (cell.CellType){case CellType.String:row[j] = cell.StringCellValue;break;case CellType.Numeric:if (DateUtil.IsCellDateFormatted(cell)){row[j] = cell.DateCellValue;}else{row[j] = cell.NumericCellValue;}break;case CellType.Boolean:row[j] = cell.BooleanCellValue;break;case CellType.Formula:// 处理公式单元格,获取计算结果row[j] = cell.CachedFormulaResultType == CellType.String? cell.StringCellValue: cell.NumericCellValue.ToString();break;default:row[j] = cell.ToString();break;}}}dataTable.Rows.Add(row);}}return dataTable;
}