jk's notes
  • 使用 NPOI 读取 xlsx 文件

使用 NPOI 读取 xlsx 文件

参考网站:

  • NuGet: https://www.nuget.org/packages/NPOI
  • GitHub: https://github.com/nissl-lab/npoi

NPOI 没有官方文档, 都是通过 Demo 来传递用法的.

Excel 文件结构

  • 一个 Excel 文件被称为 Workbook.
  • 每一个 Workbook 中包含很多表格, 每一个表格被称为 Sheet.
  • 每一个 Sheet 中有很多行 (Row), 每一行中又有很多单元格 (Cell).

这构成了 Excel 的基本结构.

读取 Excel 文件

读取 Excel:

IWorkbook workbook = WorkbookFactory.Create("文件路径");
// 或
IWorkbook workbook = WorkbookFactory.Create(<stream>);

image-20231211163529893

获得 Sheet 的方法有两种: 通过索引, 通过名字.

  • GetSheet() 方法来获得对应名字的 Sheet
  • GetSheetAt() 方法来获得对应索引的 Sheet

image-20231211163541519

与之对应的还有获得所有名字的方法, 以及获得 Sheet 个数的属性等.

image-20231211165516887

读取行与单元格

Excel 每一个表格中的行与单元格 (可简单视为列) 是自由格式. 即

  • 从第几行开始到第几行结束不确定.
  • 在指定行范围内存在空数据.
  • 每一行的数据, 单元格也是自由的, 不确定从第几个单元格开始, 到第几个单元格结束.

将 Sheet 可以看成一个稀疏矩阵.

需要注意的是, Office 和 WPS 在处理行的时候, 有些时候不同, 读取需要进行校验.

所以读取行, 需要一个范围

  • FirstRowNum, 0-base 的索引.
  • LastRowNum, 0-base 的索引.
  • 使用 ISheet 的 GetRow() 方法来获得对应索引的行对象. 注意可能返回 null.
ISheet sheet0 = workbook.GetSheetAt(0);
for (var rowIndex = sheet0.FirstRowNum; rowIndex < sheet0.LastRowNum; rowIndex++) { 
    IRow row = sheet0.GetRow(rowIndex);
}

image-20231211170340422

需要注意的是, Row 索引的取值范围是闭区间.

单元格的读取逻辑是一样的. 每一个 IRow 都有:

  • FirstCellNum
  • LastCellNum
  • GetCell() 方法

image-20231211170640796

但需要注意的是, Cell 的索引也是 0-base, 也是稀疏的. 而 Cell 的索引取值范围是左闭右开区间.

读取单元格的值

单元格默认有几种数据类型:

image-20231212085338479

常见的是: 字符串, boolean, 以及数字. 并且每一个数据类型对应不同的数据读取方式: ***CellValue.

  • 如果是字符串类型, 需要通过 StringCellValue 来取值.
  • 如果是数字类型, 需要通过 NumericCellValue 来取值.
  • 等等.

如果使用错误的方式是无法取到数据的.

如果该单元格有公式, 取值得到的是公式的值, 而公式本身也以字符串形式存储在 CellFormula 中, 例如:

image-20231212090240547

image-20231212090140654

可以将其整理成扩展方法, 例如:

public static string GetString(this ICell cell) {
    switch (cell.CellType) {
        case CellType.String: return cell.StringCellValue;
        case CellType.Boolean: return cell.BooleanCellValue.ToString();
        case CellType.Numeric: return cell.NumericCellValue.ToString();
        case CellType.Blank: return string.Empty;
        default: return string.Empty;
    }
}
Last Updated:
Contributors: jk