【.net core】【NPOI】读取表格信息(处理合并行表格数据)

发布于:2025-09-01 ⋅ 阅读:(17) ⋅ 点赞:(0)

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");
                else
                    return 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;
}


网站公告

今日签到

点亮在社区的每一天
去签到