[C#] 使用 NPOI 將結構 (Struct) 陣列轉存至 Excel 檔案

2022.11.10 / Dev
如果只是要純粹將陣列內容輸出成 Excel 能讀的檔案,那直接參考 使用 C# 將資料匯出為 CSV 中的方法三就可以快速達成。但因為 CSV 檔案僅透過逗號與分行組成,當資料

如果只是要純粹將陣列內容輸出成 Excel 能讀的檔案,那直接參考 使用 C# 將資料匯出為 CSV 中的方法三就可以快速達成。但因為 CSV 檔案僅透過逗號與分行組成,當資料內容含有特殊符號時會發生錯誤,且後續不能進行圖表繪製與使用其他 Excel 功能,對於複雜資料和資料分析等需求來說,僅使用 CSV 無法滿足要求。

C# 是基於 .NET 框架的程式語言,對於處理 Excel 檔案部分,目前於市面上有付費的 Spire.XLS.NET Excel Framework 與開源免費的 NPOI。在這篇文章中,我們將要透過 Visual Studio 來使用 NPOI 套件,將結構陣列輸出至 Excel 檔案。

安裝

首先我們要用 NuGet 來為專案安裝 NPOI 套件,打開你的專案後,從頂部導覽列的工具,進入 NuGet 套件管理員 > 管理方案的 NuGet 套件。

接下來直接搜尋 NPOI,找到下圖中最高下載數且作者是 NPOI Contributors 的套件並安裝。不要安裝到 DotNetCore.NPOI,據作者所說,DotNetCore.NPOI 是中國 NCC (China .NET Core Community) 所為,詳細資訊可參考中文翻譯版 Dotnetcore.NPOI项目的真相 或英文原文 The real history of Dotnetcore.NPOI

新增引用

開始撰寫主要功能前,必須先引用 NPOI 命名空間。請在原始碼檔案最前面加入以下引用。

using NPOI.XSSF.UserModel;

撰寫功能

緊接著要來開始撰寫主要的程式功能,主要流程如下:

為了程式的擴充性,在建立標題與寫入資料時我會使用 Reflection 的方式來取得結構中的屬性名稱與值,後續可以把此段程式碼改寫成泛型類別,一勞永逸。如果你目前只是要一個簡單的資料輸出的話,也可以於建立標題與寫入資料時直接指定屬性。

建立假資料

我們先定義 Wave 結構作為範例。其中有皆為整數型別的 Id 與 Value 屬性,後續我們還可以沿用此結構來繪製圖表。

struct Wave
{
    public int Id { get; set; }
    public int Value { get; set; }

    public Wave(int id, int value)
    {
        Id = id;
        Value = value;
    }
}

接下來建立 Wave 陣列並填入有序的 Id 與範圍 1~100 內的隨機 Value。

var waves = new Wave[100];
for (int i = 0; i < 100; i++)
{
    waves[i] = new Wave(i + 1, new Random().Next(0, 100));
}

建立活頁簿與工作表

再來的內容就跟我們實際操作 Excel 軟體一樣,先建立活頁簿檔案,再新增與命名工作表。工作表能建立多個,可自行用變數儲存起來。

var workbook = new XSSFWorkbook();
var sheet = workbook.CreateSheet("A Wave List");

建立標題

進入重點,首先透過 Type.GetProperties 與 Linq 將結構的屬性名稱提取出來,並將它們插入至工作表的第 0 行中。

詳細的 Reflection 用法可以參考 Type.GetProperties 方法 (System)PropertyInfo 類別 (System.Reflection) 的範例程式。

var propertyNames = typeof(Wave).GetProperties().Select(x => x.Name).ToArray();
var header = sheet.CreateRow(0);
for (int i = 0; i < propertyNames.Length; i++)
{
    header.CreateCell(i).SetCellValue(propertyNames[i]);
}

寫入資料

我們用雙迴圈來新增資料列,注意這裡的 CreateRow 內的數字不能跟標題列 (Index = 0) 重複,否則資料會被覆蓋。

第二層迴圈一樣使用 Reflection 的方式提取屬性值,並寫入當前的資料列中。這邊我用了 Convert.ToInt32 方法,是因為我確定我的資料都是整數值,如果你的資料是其他型別的話,可能就要另外去設計 Attribute 來保持其可擴充性,可參考 Attribute 類別 (System)

for (int i = 0; i < waves.Length; i++)
{
    var row = sheet.CreateRow(i + 1);
    for (int j = 0; j < propertyNames.Length; j++)
    {
        var value = typeof(Wave).GetProperty(propertyNames[j]).GetValue(waves[i]);
        row.CreateCell(j).SetCellValue(Convert.ToInt32(value));
    }
}

存檔輸出

最後即是存檔輸出,這邊用最簡單的 File.Create 建立一個 .xlsx 檔案,再用 NPOI 活頁簿內建的 Write 方法來寫入。

建置專案並執行後,我們就可以在與執行檔相同的資料夾中找到輸出的 Excel 檔案,大功告成!

using (var fileStream = File.Create($"{DateTime.Now.ToString("HHmmss")}.xlsx"))
{
    workbook.Write(fileStream);
}

參考資料

相關文章

Ted Liou

雲科碩士在讀中,專注於 Unity C#、TouchDesigner 技術,常把技術筆記分享到部落格,偶爾還直接挪用文章來當教材的研究生。