[C#] NPOI根據不同的資料格式填入不同的值

  • 12225
  • 0
  • 2022-02-25

[NPOI] 根據不同的資料格式填入不同的值,以及公式型態儲存格應該如何取值

NPOI是一套十分強大的Excel轉換套件

他可以讀也可以寫

雖然組裝及讀取表格的時候稍嫌麻煩

但也是因為這樣所以自由度比其他套件大上許多


這次是因為遇到了匯入的需求

使用者需要將Excel表格匯入

但欄位的屬性卻非常多種

有基本的文字、數值、日期、公式

以下就簡單分享我所寫的程式

 

Step 1: 宣告FormulaEvaluator

處理公式的時候需要用到FormulaEvaluator

但他會隨著傳入的Excel格式不一樣而要使用不同的物件

所以要在一開始判斷傳入格式宣告Workbook物件的時候一起宣告起來

#region 讀取Excel檔案
IWorkbook workbook;
IFormulaEvaluator formulaEvaluator;
using (FileStream filex = new FileStream(path, FileMode.Open, FileAccess.Read))
{
    if (Path.GetExtension(path) == ".xls")
    {   // xls
        workbook = new HSSFWorkbook(filex);
        formulaEvaluator = new HSSFFormulaEvaluator(workbook); // Important!! 取公式值的時候會用到
    }
    else if (Path.GetExtension(path) == ".xlsx")
    {   // xlsx
        workbook = new XSSFWorkbook(filex);
        formulaEvaluator = new XSSFFormulaEvaluator(workbook); // Important!! 取公式值的時候會用到
    }
    else
    {
        return msg = "檔案格式錯誤。";
    }
}
var st = workbook.GetSheetAt(0);
#endregion

 

Step 2: 取得儲存格的值

foreach (var cell in row)
{
    string columnStr = string.Empty;

    switch (cell.CellType)
    {
        case CellType.Numeric:  // 數值格式
            if (DateUtil.IsCellDateFormatted(cell))
            {   // 日期格式
                columnStr = cell.DateCellValue.ToString();
            }
            else
            {   // 數值格式
                columnStr = cell.NumericCellValue.ToString();
            }
            break;
        case CellType.String:   // 字串格式
            columnStr = cell.StringCellValue;
            break;
        case CellType.Formula:  // 公式格式
            var formulaValue = formulaEvaluator.Evaluate(cell);
            if (formulaValue.CellType == CellType.String) columnStr = formulaValue.StringValue.ToString();          // 執行公式後的值為字串型態
            else if (formulaValue.CellType == CellType.Numeric) columnStr = formulaValue.NumberValue.ToString();    // 執行公式後的值為數字型態
            break;
        default:
            break;
    }
}

 

字串格式就很基本不講了

數值格式

數值格式的話

因為日期與數字都會被判斷為數值格式

所以我在內部加入了DateUtil.IsCellDateFormatted(cell)這個判斷式

他的說明是Check if a cell Contains a date Since dates are stored internally in Excel as double values we infer it Is a date if it Is formatted as such.

簡單來說就是他會去判斷那個值是否為精確日期的刻度(就是全部都是數字的那個格式啦)

如果是的話他就會回傳true

這樣就可以判斷要轉成DateCellValue(日期)還是NumericCellValue(數字)了

公式格式

有時候User上傳怎麼傳就是傳不上去

打開檔案乍看之下格式也都正確

點進去才發現他原來是公式

那公式要如何取值?

其實就是把他判斷為NumericCellValue就好了

不過這應該只是針對值是數值型的公式

若是字串型的就要另外再試了

因為我也還沒遇到這樣的需求

所以就待之後遇到補上吧

2017/02/21編輯:

前一天才剛打完就遇到需求了

其實當時也有找到類似的資料(忘記在哪看到了)

如果有這樣的需求時就必須加入一些判斷

首先依照不同的Excel不同而建立不同的公式計算物件(一樣就是XSSF跟HSSF的差別)

再來就是公式的部分

formulaEvaluator.Evaluate(cell)先用這個把公式的值計算出來

計算後他其實就會賦予這個值一個CellType

再用這個CellType來取值(就跟Step2的取值方式一樣了)

這樣就可以取到正確的值了

 

2017/02/23編輯:

若使用公式計算會出現一個問題

如果公式內有參考其他Excel

就會因為找不到檔案所以報錯

所以我又找了另外一個方法

if (cell.CachedFormulaResultType == CellType.String) columnStr = cell.StringCellValue;
else if (cell.CachedFormulaResultType == CellType.Numeric) columnStr = cell.NumericCellValue.ToString();
else columnStr = "";

當這個儲存格格式為Formula時

我可以去取他的CachedFormulaResultType

也就是這個公式計算後的暫存值的資料型別

Excel會將公式計算的結果暫存在檔案內

所以即使收到檔案的對方沒有參考來源檔

也可以讀到正確的數值

那麼我們就可以利用判斷暫存值型別

來去針對型別取他的暫存值

利用StringCellValueNumericCellValue

不用經過計算即可正確取得暫存於檔案的數值

 

參考資料

https://dotblogs.com.tw/mis2000lab/2011/06/09/npoi_excel_formula_value

更新紀錄

2017/02/21 修改公式取值部分

2017/02/23 修改公式取值若資料內容為參考其他Excel會錯誤

Write By Charley Chang 


新手發文,若有錯誤還請指教,
歡迎留言或Mail✉給我

創用 CC 授權條款


本著作係採用創用 CC 姓名標示-非商業性-相同方式分享 4.0 國際 授權條款授權.