首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C# 實現 Excel 多欄位連續相同資料自動合併儲存格

     編輯:關於C語言

需求場景

匯出報表時,若多欄位連續列具有相同值(如部門、類別、區域),需自動合併儲存格以提升可讀性,如下圖所示:

合併儲存格效果圖

核心演算法邏輯

逐欄掃描,比較當前列與下一列在「從第一欄到當前欄」的組合值是否相同,相同則累加合併計數,不同則執行合併並重置計數。

完整實作程式碼

using Excel = Microsoft.Office.Interop.Excel;

public class ExcelMerger
{
    private Excel.Worksheet _worksheet;

    public ExcelMerger(Excel.Worksheet worksheet)
    {
        _worksheet = worksheet;
    }

    /// 
    /// 合併指定範圍內連續相同值的儲存格
    /// 
    /// 工作表索引 (1-based)
    /// 起始列索引 (1-based)
    /// 起始欄索引 (1-based)
    /// 要處理的列數
    /// 要處理的欄數
    public void MergeContinuousCells(int sheetIndex, int startRow, int startCol, int rowCount, int colCount)
    {
        if (rowCount < 1 || colCount < 1) return;

        for (int colOffset = 0; colOffset < colCount; colOffset++)
        {
            int markRow = 0;           // 當前合併區塊起始列(相對偏移)
            int mergeCount = 1;        // 連續相同列數

            for (int rowOffset = 0; rowOffset < rowCount; rowOffset++)
            {
                bool isLastRow = (rowOffset == rowCount - 1);

                // 建立「從第 0 欄到當前欄」的組合鍵值
                string currentKey = BuildCompositeKey(startRow + rowOffset, startCol, colOffset);
                string nextKey = isLastRow ? null : BuildCompositeKey(startRow + rowOffset + 1, startCol, colOffset);

                if (!isLastRow && currentKey == nextKey)
                {
                    mergeCount++;
                }
                else
                {
                    // 執行合併:從 markRow 開始,共 mergeCount 列
                    if (mergeCount > 1)
                    {
                        int actualStartRow = startRow + markRow;
                        int actualEndRow = actualStartRow + mergeCount - 1;
                        int actualCol = startCol + colOffset;
                        
                        MergeRange(actualStartRow, actualCol, actualEndRow, actualCol);
                    }

                    // 重置下一區塊
                    markRow = rowOffset + 1;
                    mergeCount = 1;
                }
            }
        }
    }

    private string BuildCompositeKey(int row, int startCol, int endColOffset)
    {
        var parts = new List();
        for (int c = 0; c <= endColOffset; c++)
        {
            var cell = (Excel.Range)_worksheet.Cells[row, startCol + c];
            // 取得合併區域左上角儲存格的值(避免重複讀取已合併儲存格)
            var topLeft = (Excel.Range)cell.MergeArea.Item[1, 1];
            parts.Add(topLeft.Text?.ToString() ?? "");
        }
        return string.Join("|", parts);
    }

    private void MergeRange(int startRow, int startCol, int endRow, int endCol)
    {
        var range = _worksheet.Range[
            _worksheet.Cells[startRow, startCol],
            _worksheet.Cells[endRow, endCol]
        ];
        range.Merge();
        range.VerticalAlignment = Excel.XlVAlign.xlVAlignCenter;
        range.HorizontalAlignment = Excel.XlHAlign.xlHAlignCenter;
    }
}

使用範例

var app = new Excel.Application();
var wb = app.Workbooks.Open(templatePath);
var ws = (Excel.Worksheet)wb.Worksheets[1];

// 假設資料從第 2 列開始,共 100 列,5 欄 (A-E),要合併前 3 欄 (A-C)
var merger = new ExcelMerger(ws);
merger.MergeContinuousCells(1, 2, 1, 100, 3);

wb.SaveAs(outputPath);
wb.Close();
app.Quit();

// 釋放 COM 物件...

關鍵技術細節

  • 組合鍵比較:使用 | 分隔多欄值,確保「部門=A, 類別=X」不會誤判為「部門=AX, 類別=空」
  • MergeArea 處理:讀取值時取 MergeArea.Item[1,1],避免重複讀取已合併區域導致邏輯錯誤
  • 效能考量:大量資料建議先讀入陣列 object[,] data = range.Value,記憶體中計算合併區間,再一次性呼叫 Merge()
  • 相容性:適用於 .xls/.xlsx,需參考 Microsoft.Office.Interop.Excel

現代替代方案

使用 ClosedXML 或 EPPlus 可在無 Office 環境下實現相同功能,語法更簡潔:

// ClosedXML 範例
var ws = wb.Worksheet(1);
var range = ws.Range(2, 1, 101, 3); // A2:C101
range.MergeSameCells(); // 擴充方法一鍵完成
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved