news 2026/1/12 17:29:40

C# NPOI入门指南:轻松操作Excel

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C# NPOI入门指南:轻松操作Excel

目录

一、NPOI 核心原理(通俗版)

1. 什么是 NPOI?

2. NPOI 核心对象模型(类比理解)

3. 核心逻辑:流操作

二、环境准备(初学者第一步)

三、高频用法(带完整示例,极简版)

用法 1:创建空白 Excel 并写入数据(最基础)

用法 2:读取已有 Excel 数据

用法 3:单元格类型处理(初学者避坑重点)

用法 4:设置列宽 / 行高(优化显示)

用法 5:批量写入数据(实战常用)

四、初学者避坑指南(高频错误)

五、完整工具类(初学者可直接复用)

总结


NPOI 是 .NET 平台下免费、开源的 Excel 操作库,无需安装 Microsoft Office,支持.xls(Excel 97-2003)和.xlsx(Excel 2007+)格式,是 C# 处理 Excel 最常用的工具。本文从核心原理入手,结合极简示例讲解高频用法,适配初学者理解节奏。

一、NPOI 核心原理(通俗版)

1. 什么是 NPOI?

NPOI 是 Apache POI(Java 版 Excel 操作库)的 C# 移植版本,核心能力:

  • 无需 Office 环境,跨 Windows/Linux(.NET Core/.NET Framework 均支持);
  • 支持 Excel 读写、样式设置、公式计算(基础);
  • 区分两种格式:HSSF对应.xlsXSSF对应.xlsx(初学者重点记这两个类前缀)。

2. NPOI 核心对象模型(类比理解)

Excel 文件的层级结构,对应 NPOI 的核心对象,初学者可类比 “书籍结构” 理解:

Excel 结构NPOI 核心对象类比书籍作用说明
整个 Excel 文件IWorkbook(接口)整本书代表一个 Excel 文件,是入口
工作表(Sheet)ISheet书籍的章节一个 Workbook 可包含多个 Sheet
IRow章节里的行一个 Sheet 包含多行,索引从 0 开始
单元格ICell行里的文字格一个 Row 包含多个单元格,索引从 0 开始

3. 核心逻辑:流操作

NPOI 操作 Excel 本质是文件流(FileStream)处理:

  • 读取 Excel:从文件流加载到IWorkbook,解析为 Sheet/Row/Cell;
  • 写入 Excel:将IWorkbook内容写入文件流,生成物理文件。

二、环境准备(初学者第一步)

通过 NuGet 安装 NPOI(Visual Studio 操作):

  1. 右键项目 → 管理 NuGet 程序包;
  2. 搜索NPOI,安装最新稳定版(建议 2.5+);
  3. 核心命名空间(必加):
// 基础核心(所有操作都需要) using NPOI.SS.UserModel; // 处理 .xls(HSSF) using NPOI.HSSF.UserModel; // 处理 .xlsx(XSSF) using NPOI.XSSF.UserModel; // 文件流处理 using System.IO;

三、高频用法(带完整示例,极简版)

用法 1:创建空白 Excel 并写入数据(最基础)

需求:创建一个测试.xlsx,写入表头 + 1 行数据(姓名、年龄、是否学生)。

// 步骤1:创建 Workbook(根据格式选 XSSF/HSSF) IWorkbook workbook = new XSSFWorkbook(); // .xlsx 用 XSSF,.xls 用 HSSF // 步骤2:创建 Sheet(指定名称) ISheet sheet = workbook.CreateSheet("学生信息"); // 步骤3:创建行(表头行,索引 0) IRow headerRow = sheet.CreateRow(0); // 步骤4:创建单元格并赋值(表头) headerRow.CreateCell(0).SetCellValue("姓名"); headerRow.CreateCell(1).SetCellValue("年龄"); headerRow.CreateCell(2).SetCellValue("是否学生"); // 步骤5:创建数据行(索引 1) IRow dataRow = sheet.CreateRow(1); dataRow.CreateCell(0).SetCellValue("张三"); dataRow.CreateCell(1).SetCellValue(20); // 数字类型 dataRow.CreateCell(2).SetCellValue(true); // 布尔类型 // 步骤6:写入文件流(核心:using 自动释放流,避免文件占用) string filePath = "测试.xlsx"; using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write)) { workbook.Write(fs); // 将 Workbook 写入流 } Console.WriteLine("Excel 创建成功!");

用法 2:读取已有 Excel 数据

需求:读取上面创建的测试.xlsx,输出所有单元格内容。

string filePath = "测试.xlsx"; // 步骤1:打开文件流(读模式) using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read)) { // 步骤2:根据文件格式加载 Workbook(初学者可简化:判断后缀) IWorkbook workbook = filePath.EndsWith(".xlsx") ? new XSSFWorkbook(fs) : new HSSFWorkbook(fs); // 步骤3:获取第一个 Sheet(索引 0) ISheet sheet = workbook.GetSheetAt(0); // 步骤4:遍历所有行(从 0 到最后一行索引) for (int i = 0; i <= sheet.LastRowNum; i++) { IRow row = sheet.GetRow(i); // 获取第 i 行 if (row == null) continue; // 跳过空行(初学者必加,避免空引用) // 步骤5:遍历当前行的所有单元格 for (int j = 0; j < row.LastCellNum; j++) { ICell cell = row.GetCell(j); // 获取第 j 列单元格 if (cell == null) { Console.Write("空值\t"); continue; } // 步骤6:根据单元格类型读取值(初学者核心:避免类型转换错) string cellValue = cell.CellType switch { CellType.String => cell.StringCellValue, // 字符串 CellType.Numeric => cell.NumericCellValue.ToString(), // 数字/日期 CellType.Boolean => cell.BooleanCellValue.ToString(), // 布尔 _ => "未知类型" // 其他类型(公式、空等) }; Console.Write(cellValue + "\t"); } Console.WriteLine(); // 换行 } } Console.WriteLine("Excel 读取完成!");

用法 3:单元格类型处理(初学者避坑重点)

Excel 单元格有多种类型(字符串、数字、日期、布尔、公式),直接强转容易报错,推荐安全读取方法

/// <summary> /// 安全读取单元格值(适配所有类型,返回字符串) /// </summary> /// <param name="cell">单元格</param> /// <param name="defaultValue">默认值(单元格为空/异常时返回)</param> private static string GetCellValue(ICell cell, string defaultValue = "") { if (cell == null) return defaultValue; return cell.CellType switch { CellType.String => cell.StringCellValue.Trim(), // 字符串去空格 CellType.Numeric => // 区分数字和日期(NPOI 中日期也属于 Numeric 类型) DateUtil.IsCellDateFormatted(cell) ? cell.DateCellValue.ToString("yyyy-MM-dd") : cell.NumericCellValue.ToString(), CellType.Boolean => cell.BooleanCellValue ? "是" : "否", // 转中文更友好 CellType.Formula => // 公式类型:读取计算结果 cell.CachedFormulaResultType == CellType.Numeric ? cell.NumericCellValue.ToString() : cell.StringCellValue, _ => defaultValue }; }

使用示例

// 读取年龄(转int) int age = int.TryParse(GetCellValue(row.GetCell(1)), out int res) ? res : 0; // 读取是否学生(转bool) bool isStudent = GetCellValue(row.GetCell(2)) == "是";

用法 4:设置列宽 / 行高(优化显示)

NPOI 列宽单位是「256 × 字符宽度」,行高单位是「1/20 点」,初学者直接传数字会显示异常:

ISheet sheet = workbook.CreateSheet("学生信息"); // 设置列宽:第0列(姓名)宽度为 10 个字符 sheet.SetColumnWidth(0, 10 * 256); // 第1列(年龄)宽度为 8 个字符 sheet.SetColumnWidth(1, 8 * 256); // 设置行高:表头行高 30px(30×20) IRow headerRow = sheet.CreateRow(0); headerRow.Height = 30 * 20;

用法 5:批量写入数据(实战常用)

需求:将列表数据批量写入 Excel:

// 模拟数据 List<Student> studentList = new List<Student>() { new Student { Name = "张三", Age = 20, IsStudent = true }, new Student { Name = "李四", Age = 22, IsStudent = false }, new Student { Name = "王五", Age = 19, IsStudent = true } }; // 创建Workbook和Sheet IWorkbook workbook = new XSSFWorkbook(); ISheet sheet = workbook.CreateSheet("批量数据"); // 写入表头 IRow headerRow = sheet.CreateRow(0); headerRow.CreateCell(0).SetCellValue("姓名"); headerRow.CreateCell(1).SetCellValue("年龄"); headerRow.CreateCell(2).SetCellValue("是否学生"); // 批量写入数据行 for (int i = 0; i < studentList.Count; i++) { IRow row = sheet.CreateRow(i + 1); // 数据行从索引1开始 var student = studentList[i]; row.CreateCell(0).SetCellValue(student.Name); row.CreateCell(1).SetCellValue(student.Age); row.CreateCell(2).SetCellValue(student.IsStudent ? "是" : "否"); } // 保存文件 using (FileStream fs = new FileStream("批量数据.xlsx", FileMode.Create, FileAccess.Write)) { workbook.Write(fs); } // 定义学生类 public class Student { public string Name { get; set; } public int Age { get; set; } public bool IsStudent { get; set; } }

四、初学者避坑指南(高频错误)

  1. Sheet 索引越界GetSheetAt(0)报错 → 检查 Excel 是否有 Sheet,或创建 Workbook 后是否手动创建了 Sheet(空 Workbook 无默认 Sheet);
  2. 文件占用:报错 “进程无法访问文件” → 确保所有FileStreamusing包裹(自动释放句柄),或关闭 Excel 后再运行程序;
  3. 类型转换错:读取数字时报 “无法将 double 转 int” → 用GetCellValue先转字符串,再用int.TryParse
  4. 空引用异常row.GetCell(j)报错 → 先判断row != nullcell != null
  5. 格式不匹配:用HSSFWorkbook处理.xlsx→ 严格区分:.xlsHSSF.xlsxXSSF

五、完整工具类(初学者可直接复用)

封装极简版 Excel 读写工具类,覆盖 80% 基础场景:

public static class SimpleExcelHelper { /// <summary> /// 写入数据到Excel /// </summary> /// <param name="filePath">保存路径(.xls/.xlsx)</param> /// <param name="sheetName">工作表名称</param> /// <param name="headers">表头列表</param> /// <param name="dataList">数据列表(每行是一个字符串数组)</param> public static void WriteExcel(string filePath, string sheetName, List<string> headers, List<string[]> dataList) { // 创建Workbook IWorkbook workbook = filePath.EndsWith(".xlsx") ? new XSSFWorkbook() : new HSSFWorkbook(); ISheet sheet = workbook.CreateSheet(sheetName); // 写入表头 IRow headerRow = sheet.CreateRow(0); for (int i = 0; i < headers.Count; i++) { headerRow.CreateCell(i).SetCellValue(headers[i]); sheet.SetColumnWidth(i, 12 * 256); // 统一列宽 } // 写入数据 for (int i = 0; i < dataList.Count; i++) { IRow row = sheet.CreateRow(i + 1); string[] rowData = dataList[i]; for (int j = 0; j < rowData.Length; j++) { row.CreateCell(j).SetCellValue(rowData[j]); } } // 保存文件 using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write)) { workbook.Write(fs); } } /// <summary> /// 读取Excel数据 /// </summary> /// <param name="filePath">文件路径</param> /// <returns>表头+所有数据(第一行是表头)</returns> public static List<string[]> ReadExcel(string filePath) { var result = new List<string[]>(); using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read)) { IWorkbook workbook = filePath.EndsWith(".xlsx") ? new XSSFWorkbook(fs) : new HSSFWorkbook(fs); ISheet sheet = workbook.GetSheetAt(0); for (int i = 0; i <= sheet.LastRowNum; i++) { IRow row = sheet.GetRow(i); if (row == null) continue; string[] rowData = new string[row.LastCellNum]; for (int j = 0; j < row.LastCellNum; j++) { rowData[j] = GetCellValue(row.GetCell(j)); } result.Add(rowData); } } return result; } /// <summary> /// 安全读取单元格值 /// </summary> private static string GetCellValue(ICell cell) { if (cell == null) return ""; return cell.CellType switch { CellType.String => cell.StringCellValue.Trim(), CellType.Numeric => DateUtil.IsCellDateFormatted(cell) ? cell.DateCellValue.ToString("yyyy-MM-dd") : cell.NumericCellValue.ToString(), CellType.Boolean => cell.BooleanCellValue ? "是" : "否", _ => "" }; } }

使用示例

// 写入示例 var headers = new List<string> { "姓名", "年龄", "城市" }; var data = new List<string[]>() { new[] { "张三", "20", "北京" }, new[] { "李四", "22", "上海" } }; SimpleExcelHelper.WriteExcel("工具类测试.xlsx", "测试数据", headers, data); // 读取示例 var readData = SimpleExcelHelper.ReadExcel("工具类测试.xlsx"); foreach (var row in readData) { Console.WriteLine(string.Join("\t", row)); }

总结

初学者掌握 NPOI 核心:记住 Workbook→Sheet→Row→Cell 的层级用 using 管理文件流按单元格类型读取值,就能覆盖绝大多数基础场景。先从 “创建 / 读取空白 Excel” 入手,再逐步扩展样式、批量数据等进阶用法,避免一开始陷入复杂逻辑。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/1/9 12:34:07

【R-Python模型融合实战】:揭秘跨平台建模结果验证的5大核心步骤

第一章&#xff1a;R-Python模型融合结果验证概述在跨语言建模日益普及的背景下&#xff0c;R 与 Python 的模型融合已成为数据科学工作流中的关键环节。二者分别在统计分析与机器学习工程化方面具备独特优势&#xff0c;通过整合 R 的高级统计包&#xff08;如 lme4、survival…

作者头像 李华
网站建设 2026/1/11 22:38:18

从田间到R控制台,方差分析如何改变传统农业决策?

第一章&#xff1a;从田间到R控制台——方差分析在农业决策中的角色在现代农业研究中&#xff0c;科学决策依赖于对实验数据的严谨分析。当农学家需要比较不同施肥方案、作物品种或灌溉策略对产量的影响时&#xff0c;方差分析&#xff08;ANOVA&#xff09;成为核心统计工具。…

作者头像 李华
网站建设 2026/1/11 22:30:49

2025年最新阿勒泰地区道路矢量数据

2025年阿勒泰地区道路shp数据&#xff08;含处理代码&#xff09;&#xff0c;数据坐标系wgs84坐标系&#xff0c; 数据shp格式&#xff0c;.cpg&#xff0c;.dbf&#xff0c;.prj&#xff0c;.shp&#xff0c;.shx&#xff0c;并通过python代码进行裁剪。 一、总体规模与网络格…

作者头像 李华
网站建设 2026/1/11 9:12:30

设计模式[10]——外观模式一分钟彻底说清楚

设计模式[10]——外观模式&#xff08;Facade&#xff09;一分钟彻底说透&#xff08;C版软件领域真实例子&#xff09; 一句话定义 为一个复杂子系统提供一个简洁、高层接口&#xff0c;隐藏内部的复杂性&#xff0c;让客户端“一键启动”或“一键操作”整个系统。 最狠的比喻…

作者头像 李华
网站建设 2026/1/9 2:13:44

Temu 分销重塑跨境生态:轻资产时代的新增长法则

库存与资金的沉重枷锁&#xff0c;以及全球履约的复杂迷宫&#xff0c;始终是跨境卖家增长道路上的主要障碍&#xff0c;在此背景下&#xff0c;Temu全球分销平台的推出&#xff0c;标志着一场“轻资产”运营革命的兴起&#xff0c;它并非简单的选品工具&#xff0c;而是一套系…

作者头像 李华