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

C# 使用 NPOI 讀寫 Excel (xls/xlsx) 完整教學與進階技巧

     編輯:關於C#

為什麼選擇 NPOI?

NPOI 是 Apache POI 專案的 .NET 移植版,純 managed code 實現,無需安裝 Office 即可操作 Excel、Word、PowerPoint。優點:

  • 同時支援 .xls (BIFF8) 與 .xlsx (OOXML) 格式
  • 部署輕量(DLL 僅數 MB),無 COM Interop 相容性問題
  • 開源免費,社群活躍,文件豐富

統一介面處理 xls/xlsx

NPOI 使用 HSSFWorkbook (xls) 與 XSSFWorkbook (xlsx),兩者皆實作 IWorkbook 介面,可透過副檔名動態選擇:

IWorkbook workbook;
string ext = Path.GetExtension(filePath).ToLower();
using (var fs = File.OpenRead(filePath))
{
    workbook = ext == ".xls" ? new HSSFWorkbook(fs) : new XSSFWorkbook(fs);
}

讀取 Excel 完整範例

public DataTable ReadExcel(string filePath)
{
    IWorkbook wk;
    string ext = Path.GetExtension(filePath).ToLower();
    using (var fs = File.OpenRead(filePath))
    {
        wk = ext == ".xls" ? new HSSFWorkbook(fs) : new XSSFWorkbook(fs);
    }

    var sheet = wk.GetSheetAt(0); // 取第一個工作表
    var table = new DataTable();

    // 表頭
    var headerRow = sheet.GetRow(0);
    if (headerRow != null)
    {
        for (int c = 0; c < headerRow.LastCellNum; c++)
            table.Columns.Add(headerRow.GetCell(c)?.ToString() ?? $"Column{c}");
    }

    // 資料列
    for (int r = (headerRow != null ? 1 : 0); r <= sheet.LastRowNum; r++)
    {
        var row = sheet.GetRow(r);
        if (row == null) continue;

        var dataRow = table.NewRow();
        for (int c = 0; c < row.LastCellNum; c++)
        {
            var cell = row.GetCell(c);
            dataRow[c] = GetCellValue(cell);
        }
        table.Rows.Add(dataRow);
    }
    return table;
}

// 根據儲存格型別取得強型別值
private object GetCellValue(ICell cell)
{
    if (cell == null || cell.CellType == CellType.Blank) return null;

    switch (cell.CellType)
    {
        case CellType.Numeric:
            return DateUtil.IsCellDateFormatted(cell)
                ? (object)cell.DateCellValue
                : cell.NumericCellValue;
        case CellType.Boolean:
            return cell.BooleanCellValue;
        case CellType.Formula:
            // 公式儲存格需依公式結果型別處理,這裡簡單回傳字串
            return cell.StringCellValue;
        default:
            return cell.StringCellValue;
    }
}

寫入 Excel 完整範例(含樣式、合併儲存格)

public void WriteExcel(string filePath)
{
    IWorkbook wb;
    string ext = Path.GetExtension(filePath).ToLower();
    wb = ext == ".xls" ? new HSSFWorkbook() : new XSSFWorkbook();

    // 樣式 1:基本框線、置中、自動換行
    var style1 = wb.CreateCellStyle();
    style1.BorderBottom = style1.BorderLeft = style1.BorderRight = style1.BorderTop = BorderStyle.Thin;
    style1.Alignment = HorizontalAlignment.Center;
    style1.VerticalAlignment = VerticalAlignment.Center;
    style1.WrapText = true;

    // 樣式 2:紅色楷體字、黃底
    var style2 = wb.CreateCellStyle();
    var font = wb.CreateFont();
    font.FontName = "楷體";
    font.Color = HSSFColor.Red.Index;
    font.Boldweight = (short)FontBoldWeight.Bold;
    style2.SetFont(font);
    style2.FillForegroundColor = HSSFColor.Yellow.Index;
    style2.FillPattern = FillPattern.SolidForeground;
    style2.Alignment = HorizontalAlignment.Center;
    style2.VerticalAlignment = VerticalAlignment.Center;
    style2.BorderBottom = style2.BorderLeft = style2.BorderRight = style2.BorderTop = BorderStyle.Thin;

    var sheet = wb.CreateSheet("Sheet0");

    // 設定欄寬(單位:1/256 字元寬度)
    int[] colWidths = { 10, 10, 10, 20 };
    for (int i = 0; i < colWidths.Length; i++)
        sheet.SetColumnWidth(i, 256 * colWidths[i]);

    // 測試資料
    object[,] data = {
        { "列0", "列1", "列2", "列3" },
        { "", 400, 5.2, 6.01 },
        { "", DateTime.Today, true, "2014-07-02" }
    };

    for (int r = 0; r < 3; r++)
    {
        var row = sheet.CreateRow(r);
        for (int c = 0; c < 4; c++)
        {
            var cell = row.CreateCell(c);
            cell.CellStyle = (c % 2 == 0) ? style1 : style2;
            SetCellValue(cell, data[r, c]);
        }
    }

    // 合併儲存格:第 0-2 列、第 0 欄
    sheet.AddMergedRegion(new CellRangeAddress(0, 2, 0, 0));

    using (var fs = File.Create(filePath))
        wb.Write(fs);
}

// 依物件型別寫入儲存格
private void SetCellValue(ICell cell, object value)
{
    if (value == null) { cell.SetCellValue(""); return; }

    switch (value)
    {
        case int i: cell.SetCellValue(i); break;
        case double d: cell.SetCellValue(d); break;
        case DateTime dt: cell.SetCellValue(dt); break;
        case bool b: cell.SetCellValue(b); break;
        case IRichTextString rt: cell.SetCellValue(rt); break;
        default: cell.SetCellValue(value.ToString()); break;
    }
}

儲存格鎖定與工作表保護

// 解鎖特定儲存格(預設皆為鎖定)
cell.CellStyle.IsLocked = false;

// 保護工作表(需提供密碼)
sheet.ProtectSheet("password");

常見坑點提醒

  • 日期儲存為 Numeric: Excel 內部以數值儲存日期,讀取時必須用 DateUtil.IsCellDateFormatted 判斷。
  • LastRowNum 是「最後一列索引」(以 0 為基底),而非列數;迴圈條件用 r <= sheet.LastRowNum。
  • LastCellNum 是「最後一欄索引 + 1」,即該列實際欄數。
  • 大量資料寫入時,建議使用 SXSSFWorkbook (xlsx 串流模式) 避免記憶體暴增。

參考資源

原文連結:https://www.bianceng.cn/Programming/csharp/201410/45750.htm

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved