ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

C# NPOI 实战:蓝墨云试题导出与多 Sheet 合并

C# NPOI 实战:蓝墨云试题导出与多 Sheet 合并 简介LmyExamExport.rar 是一套面向教育工作者与 C# 开发者的蓝墨云试题导出工具源码针对平台普通用户只能导入、无法导出试题的痛点借助 NPOI 库在不依赖 Office 的情况下读写 Excel将测试数据解析重组为完整试题库并支持是否显示答案的可选设置兼顾教师评估与学生自练场景。压缩包共 85 个文件约 17.87MB以 22 个 dll 依赖库、10 个 cs 源码、8 个 xml 配置及 resx 资源文件为主另含 exe 可执行程序、xls/xlsx 示例数据与 sln 解决方案结构完整可直接编译运行。资源已有 1167 人学习下载适合想研究 NPOI 读写 Excel、WinForm 界面封装与试题数据建模的开发者参考也可作为教育信息化工具的二次开发起点。1. 从蓝墨云到 Excel一份 C# 源码怎么把试题数据捞出来蓝墨云现改叫云班课的试题管理有个让一线教师很头疼的限制题库只能导入不能导出。你在平台上攒了几百道选择、判断、填空想备份、想迁移到别的系统、想打印成纸质卷子平台不给出口。LmyExamExport.rar 就是冲着这个缺口来的——一份用 C# 写的 WinForms 小工具核心逻辑是用 NPOI 读写 Excel把从蓝墨云拿到的测试数据转成结构化试题库还能按需决定要不要把答案一起写进去。它适合两类人一类是手里有蓝墨云试题数据、想批量转成 Excel 的教师或教务另一类是想找一个真实 NPOI 落地案例来拆的 C# 开发者。源码里带了 frmExamExport、questtype.cs、Program.cs 这些文件结构不复杂但把 NPOI 读写、多 Sheet 组织、答案开关这几件事都串起来了拿来当模板改比从零写省事得多。2. 拆开 LmyExamExportNPOI 读写与答案开关的实现骨架2.1 工程结构与依赖packages.config 里藏着什么拿到源码先别急着 F5先看依赖。LmyExamExport 用的是传统 .NET Framework 工程依赖走 packages.config 管理不是 PackageReference。打开 packages.config 能看到三个关键包NPOI 2.5.1、Portable.BouncyCastle 1.8.6、SharpZipLib 1.2.0。NPOI 负责 Excel 读写后两个是 NPOI 处理 xlsx本质是 zip 包时的底层依赖缺一个都会在打开 xlsx 时报“无法加载文件或程序集”。工程文件里还有几个值得注意的App.config 管运行时配置Settings.settings 和 Settings.Designer.cs 是项目级设置frmExamExport.cs 是主窗体逻辑questtype.cs 定义题目类型LmyExport.vdproj 是安装包工程。如果你只想跑起来看效果用 Visual Studio 打开 LmyExamExport.sln还原 NuGet 包直接编译 Debug 就行。但要注意NPOI 2.5.1 对 .NET Framework 版本有要求低于 4.5 会编译不过建议用 4.6.1 以上。!-- packages.config 关键片段 -- packages package idNPOI version2.5.1 targetFrameworknet461 / package idPortable.BouncyCastle version1.8.6 targetFrameworknet461 / package idSharpZipLib version1.2.0 targetFrameworknet461 / /packages这段配置说明三件事第一NPOI 2.5.1 是主库读写 xls/xlsx 都靠它第二BouncyCastle 是加密相关依赖NPOI 处理带密码或特定格式的 Excel 时会用到第三SharpZipLib 负责 zip 流操作xlsx 解压全靠它。版本号别随意升NPOI 2.5.x 和 2.6.x 的 API 有差异升上去可能编译报错。2.2 读数据用 NPOI 把试题从 Excel 里捞出来蓝墨云导出的数据通常是一张或多张 Excel 表字段包括题干、选项、答案、题型等。LmyExamExport 的读取逻辑集中在 frmExamExport.cs 里核心是 IWorkbook 和 ISheet 的遍历。常见做法是先用 FileStream 打开文件再用 WorkbookFactory.Create 自动识别 xls/xlsx然后逐行读单元格。// 读取试题 Excel 的核心逻辑示意基于 NPOI 2.5.1 using (FileStream fs new FileStream(filePath, FileMode.Open, FileAccess.Read)) { IWorkbook workbook WorkbookFactory.Create(fs); // 自动识别 xls/xlsx ISheet sheet workbook.GetSheetAt(0); // 取第一个 Sheet for (int i 1; i sheet.LastRowNum; i) // 从第 2 行开始跳过表头 { IRow row sheet.GetRow(i); if (row null) continue; string question row.GetCell(0)?.ToString() ?? ; // 题干 string optionA row.GetCell(1)?.ToString() ?? ; // 选项 A string answer row.GetCell(5)?.ToString() ?? ; // 答案 // 按题型封装成 questtype 对象 } }逻辑说明WorkbookFactory.Create 是 NPOI 提供的工厂方法能根据文件头自动判断是 HSSFxls还是 XSSFxlsx省去手动判断。GetCell 返回 ICell可能为 null所以用 ?. 和 ?? 做空值保护这是 NPOI 读写里最常见的翻车点——空单元格直接 ToString() 会抛 NullReferenceException。LastRowNum 是最后一行的索引从 0 开始循环时注意边界。参数上filePath 建议用绝对路径相对路径在 WinForms 里容易因为工作目录不同而找不到文件。2.3 写数据答案显示开关与多 Sheet 组织导出环节是这份源码最有价值的部分。它提供了一个“是否显示答案”的选项对应到代码里就是一个 bool 开关控制写入时是否把答案列加进去。同时如果试题量大还会按题型或来源拆成多个 Sheet避免单表过长。// 写出试题到新 Excel带答案开关 IWorkbook outBook new XSSFWorkbook(); // 新建 xlsx ISheet outSheet outBook.CreateSheet(试题库); int rowIdx 0; IRow header outSheet.CreateRow(rowIdx); header.CreateCell(0).SetCellValue(题干); header.CreateCell(1).SetCellValue(选项A); // ... 其他表头 if (showAnswer) // 答案开关 { header.CreateCell(5).SetCellValue(答案); } foreach (var q in questionList) { IRow r outSheet.CreateRow(rowIdx); r.CreateCell(0).SetCellValue(q.Question); r.CreateCell(1).SetCellValue(q.OptionA); if (showAnswer) { r.CreateCell(5).SetCellValue(q.Answer); } } using (FileStream outFs new FileStream(outPath, FileMode.Create, FileAccess.Write)) { outBook.Write(outFs); }逻辑说明XSSFWorkbook 对应 xlsx 格式HSSFWorkbook 对应 xls。如果数据量超过 65536 行必须用 XSSF否则会溢出。showAnswer 这个 bool 从窗体上的 CheckBox 读取控制表头和每行是否写答案列。注意 outBook.Write 之后不要立刻关流用 using 包住最稳妥。参数上outPath 建议带 .xlsx 后缀NPOI 不会自动补。多 Sheet 场景下CreateSheet 传不同名字即可比如“单选题”“判断题”各一个 Sheet这也是热搜里“npoi 多个 excel 合并到一个 excel 多个 sheet”的典型用法——反过来把多个来源合并到一个工作簿的多个 Sheet逻辑是一样的只是循环创建 Sheet 而已。3. 从源码到可运行工具编译、配置与导出实操3.1 环境准备与编译步骤这份源码是传统 .NET Framework WinForms 工程不是 .NET Core/.NET 5所以别用 dotnet CLI 去 build会失败。正确姿势是装 Visual Studio 2019 或 2022勾选“.NET 桌面开发”工作负载然后打开 LmyExamExport.sln。首次打开时 NuGet 包不会自动还原需要右键解决方案选“还原 NuGet 包”或者用包管理器控制台执行 Update-Package -reinstall。还原完成后Debug 配置下直接 F5 就能跑。如果编译报“找不到 NPOI”检查 packages.config 里的版本和实际 packages 文件夹是否一致。常见情况是别人拷给你的源码里 packages 文件夹是空的NuGet 还原后路径对不上这时删掉 csproj 里的 HintPath 重新还原即可。App.config 里如果有连接字符串或路径配置按你本机环境改不改也能跑只是默认路径可能指向作者机器上的目录。3.2 输入数据格式与字段映射工具要能跑通输入 Excel 的列顺序得对得上。从源码逻辑看它默认第 0 列是题干第 1 到第 4 列是选项 A-D第 5 列是答案第 6 列可能是题型或解析。如果你的蓝墨云导出格式不同要么在 Excel 里手动调列序要么改 frmExamExport.cs 里的 GetCell 索引。建议先拿一份小样本10 道题试确认字段映射无误再批量跑。列索引含义是否必填备注0题干是支持纯文本1-4选项 A-D选择题必填判断题可留空5答案是与 showAnswer 联动6题型否用于分 Sheet这张表是实操时最该先对一遍的东西。列错一位导出的题库就全乱。我一般会先在 Excel 里把表头改成中文再跑工具出问题一眼能看出来。3.3 导出参数怎么设答案开关与 Sheet 拆分窗体上那个“是否显示答案”的 CheckBox 直接绑定 showAnswer 变量。勾上导出的 Excel 多一列答案不勾答案列不写。这个设计对教师很实用给学生练习的版本不显示答案给教研组审核的版本显示答案同一份数据导两次就行。Sheet 拆分逻辑看源码里有没有按题型分组如果有导出时会自动建多个 Sheet如果没有所有题挤在一个 Sheet 里题多了翻起来累。你可以自己加一段 GroupBy 逻辑按 questtype 分组后循环 CreateSheet。// 按题型拆分 Sheet 的常见写法 var groups questionList.GroupBy(q q.Type); foreach (var g in groups) { ISheet s outBook.CreateSheet(g.Key); // Sheet 名用题型 int idx 0; // ... 写表头和行 }这段代码不是源码原文但它是这个场景下最自然的扩展。GroupBy 的 key 就是题型字符串CreateSheet 时直接当 Sheet 名。注意 Sheet 名不能超过 31 个字符也不能含 : \ / ? * [ ] 这些字符否则 NPOI 会抛异常。这是多 Sheet 导出里最容易踩的坑之一。4. 避坑与排查NPOI 读写 Excel 的五个血泪经验4.1 空单元格直接 ToString 导致崩溃现象程序读到某一行突然抛 NullReferenceException堆栈指向 GetCell(...).ToString()。原因NPOI 的 GetCell 在单元格不存在或为空时返回 null直接调 ToString 必崩。解决统一用 row.GetCell(i)?.ToString() ?? 做空值兜底或者先判断 cell ! null 再取值。这个坑几乎每个 NPOI 新手都会踩一次。4.2 xlsx 打开报“无法加载文件或程序集”现象编译通过运行时打开 xlsx 报错提示找不到 BouncyCastle 或 SharpZipLib。原因NPOI 2.5.1 处理 xlsx 依赖这两个包packages.config 里有但 bin 目录下没拷过去。解决检查引用里 NPOI、BouncyCastle、SharpZipLib 三个 DLL 是否都复制到本地csproj 里对应引用的“复制本地”设为 true。或者干脆用 NuGet 重新安装 NPOI让它自动带依赖。4.3 大数据量导出内存溢出现象导出几千道题时程序卡死或抛 OutOfMemoryException。原因XSSFWorkbook 把所有行都放在内存里数据量大时吃不消。解决数据量超过 1 万行考虑用 SXSSFWorkbookNPOI 提供的流式写它只保留部分行在内存。或者分批导出每 5000 行写一个文件。这个坑在“npoi 多个 excel 合并”场景里尤其明显合并前先评估总量。4.4 列索引错位导致答案串行现象导出的题库里答案和题目对不上A 题的答案跑到 B 题下面。原因输入 Excel 的列顺序和代码里 GetCell 的索引不一致或者中间有空列被跳过。解决先用小样本核对列映射把表头打印出来对照。代码里别硬编码索引尽量用表头名查找列号虽然麻烦但稳。4.5 文件被占用导致写入失败现象导出时提示“文件正被另一进程使用”。原因目标 Excel 文件还在 Excel 里开着或者上一次的 FileStream 没关干净。解决写文件前先判断目标是否存在存在就提示用户关闭FileStream 一律用 using 包住确保释放。WinForms 里还可以在导出前弹个确认框让用户先关掉 Excel。5. 进阶把 NPOI 多 Sheet 合并与答案开关做成可复用方法源码跑通之后真正省事的是把它抽成可复用方法。我一般会写两个静态方法一个 ReadQuestions(string path) 返回 List 一个 WriteQuestions(List list, string outPath, bool showAnswer, bool splitByType)。这样不管输入是蓝墨云还是别的平台只要列映射改一下读写逻辑不用动。多 Sheet 合并的场景也类似。热搜里常有人问“npoi 多个 excel 合并到一个 excel 多个 sheet”其实核心就是遍历文件列表每个文件读成一个 ISheet再写进同一个 IWorkbook 的不同 Sheet 里。注意 Sheet 名不能重复可以用文件名或序号做后缀。如果还要合并到一个 Sheet那就把行追加到同一个 ISheet 末尾用 sheet.LastRowNum 定位。// 多个 Excel 合并到一个工作簿的多个 Sheet IWorkbook merged new XSSFWorkbook(); foreach (string f in files) { IWorkbook wb WorkbookFactory.Create(f); ISheet src wb.GetSheetAt(0); ISheet dst merged.CreateSheet(Path.GetFileNameWithoutExtension(f)); for (int i 0; i src.LastRowNum; i) { IRow srcRow src.GetRow(i); if (srcRow null) continue; IRow dstRow dst.CreateRow(i); for (int j 0; j srcRow.LastCellNum; j) { dstRow.CreateCell(j).SetCellValue(srcRow.GetCell(j)?.ToString() ?? ); } } } using (FileStream fs new FileStream(merged.xlsx, FileMode.Create)) { merged.Write(fs); }这段代码的关键参数files 是输入文件路径数组merged 是目标工作簿CreateSheet 的参数是 Sheet 名。注意内层循环用 srcRow.LastCellNum 而不是固定列数这样不同列数的表都能兼容。SetCellValue 传字符串数字和日期会变成文本如果需要保留格式得判断 CellType 分别处理。这个细节在合并成绩表、试题表时经常被忽略导出来一看数字全变文本了。验证方法很简单合并后打开 Excel数一下 Sheet 数量对不对每个 Sheet 的行数和源文件是否一致随机抽几行核对内容。我习惯在代码里加一句 Console.WriteLine 输出每个 Sheet 的行数跑完看一眼日志比打开 Excel 数快。从那以后我每次做 NPOI 导出都强制先拿 10 行小样本跑一遍确认列映射、答案开关、Sheet 名都对了再上全量数据。这个习惯帮我省了至少三次返工。希望帮到你。本文还有配套的精品资源点击获取
返回列表