C#通过NPIO读写Excel表的方法详解
作者:火星papa
文章主要了使用POI用于读写Excel文件,对比了NPOI与Microsoft.Office.Interop.Excel的优点和缺点,文章通过代码示例展示了使用NPOI读写Excel的各类数据,以及导出ExcelN的方法,通过这些示例,读者可以了解如何使用POI用于Excel文件的操作,需要的朋友可以参考下
前言
近年一些老旧工程的发现会因为工程需要使用操作Excel表调用office的接口(Microsoft.Office.Interop.Excel接口),在部分电脑中会出现无法启动工程、报COM错误或者保存文件时偶尔闪退等异常。这个问题对应使用方确实是有些痛苦,排查起来也比较困难,可能是部分盗版系统不完整也可能是office版本,也可能是系统设置问题,如果工程代码可已重构和替换接口,则建议使用NPOI来实现,要稳定很多。本文就NPOI的优点以及如何使用它读写Excel表,并使用代码描述给大家参考。
Microsoft.Office.Interop.Excel和NPOI的对比
如下表多项对比,可以看出NPOI很是有着较大的优势,大家在读写控制Excel表格时,建议优先考虑。
| 对比项 | Microsoft.Office.Interop.Excel | NPOI |
|---|---|---|
| 环境依赖 | 必须安装 Office,Windows 独占 | 无 Office 依赖,跨平台(Windows/Linux) |
| 性能与内存 | 慢、耗内存、易泄漏;大数据易卡死 | 快、轻量、稳定;大数据更友好 |
| 并发 / 服务器 | 极差:单线程、进程残留、权限问题 | 优秀:多线程安全、纯内存 / 流操作 |
| 部署难度 | 高:需安装 Office、配置 DCOM、权限 | 低:NuGet 安装,直接部署 |
| 功能完整度 | 最全:VBA、宏、图表、透视表、函数等 | 主流功能:样式、公式、图表、合并单元格等 |
| 版本兼容 | 易出现版本冲突(2016/2019/365) | 统一 API,无版本问题 |
| 授权 | 需 Office 授权(服务器商用需额外许可) | 开源免费(Apache 2.0) |
| 适用场景 | Windows 桌面、单机自动化、需完整 Excel 特性 | 服务器导出 / 导入、Web、跨平台、大数据 |
| 常见坑 | 进程杀不掉、文件占用、权限、COM 异常 | 部分高级格式 / 函数需手动处理 |
这里使用VS2022下基于.netFramework4.8.1工程来描述NPOI的部分功能。提示: 下面案例可供参考
一、添加第三方库
在根据的net包管理器中找到管理解决方案的netGet程序包(如图所示)。

在浏览中搜索"NPOI”,可以找到人气最旺的NPIO,可以安装最新版本,点击安装即可。我这里安装了稳定版本2.7.6,已确认该版本可以使用。

- 安装成功后可以看到NuGet解决方案中已安装中就有了这个NPOI项目,并可以点击卸载。
- 在工程解决方案资源管理器的引用中也可以看到添加的NPOI项目。

二、代码描述构建函数
在工程文件中添加一个 ExcelOperation.cs的类文件,就可以开始添加对应的接口代码了。
1、添加引用
除了系统使用的其他引用务必加上NPOI的几个引用
//工具 → “管理NuGet程序包” → 搜索NPOI(Tony Qu,NPOI Contributors)→ 安装版本 2.7.6 using NPOI.HSSF.UserModel; using NPOI.SS.UserModel; using NPOI.XSSF.UserModel; using System; using System.Collections.Generic; using System.Data; using System.Diagnostics; using System.Drawing; using System.IO; using System.Runtime.InteropServices; using System.Text; using System.Windows.Forms;
2、读Excel表功能函数设计
(1) 读取EXCEL表格所有数据到字符串数组
- 可以看到这里通过数据流的方式出处Excel表个内容,表单使用的默认的第一个表单,当然你也可以以名称的方式获取表单。
- 它是通过首行和首列来获取行数和列数的,所以原始文件不要有首行首列中间为空的情况。
/// <summary>
/// 读取EXCEL表格所有数据到字符串数组
/// </summary>
/// <param name="filePath">文件路径</param>
/// <returns></returns>
public string[] ReadExcelAllToStrs(string filePath)
{
//初始化返回值
string[] strs = new string[] { };
try
{
//创建Excel文件数据流对象
FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
//创建Excel文件对象
IWorkbook workbook = new XSSFWorkbook(fileStream);
//获取第1个表单
ISheet worksheet = workbook.GetSheetAt(0);
//获取单元格值行列
int rowCount = worksheet.LastRowNum + 1;
Console.WriteLine("行数=" + rowCount);
//按照行数初始化strs元素数量
strs = new string[rowCount];
int colCount = worksheet.GetRow(0).LastCellNum;
Console.WriteLine("列数=" + colCount);
for (int i = 0; i < rowCount; i++)
{
Console.Write("第" + i + "行=\n");
IRow row = worksheet.GetRow(i);
string rowString = "";
for (int j = 0; j < colCount; j++)
{
string cellValue = $"{row.GetCell(j)}";
rowString += cellValue + "\t";
}
//获取返回值
strs[i] = (rowString);
Console.WriteLine(rowString);
}
}
catch (Exception ex)
{
Console.WriteLine("ReadExcelFile异常:" + ex.Message);
MessageBox.Show(ex.Message);
}
return strs;
}
(2) 读取EXCEL表格所有数据到字符串数组
- 同上面的方法大统小异,不同的是增加了部分限制条件,一方面,既定义了获取文件行列数的限制,也规定了只获取表中指定列的内容,适合在大表格中获取部分需要的数据。另一方面,我们固定获取指定位置的数据,转到数据结构,需要用户提供的数据列标题和内容必须严格对应,不然就会获取错误。
struct NEED_AGING_STRUCT
{
public string MacAddress;
public string AgingResult;
public string AgingTime;
}
/// <summary>
/// 读取EXCEL表格所有需要的数据到指定的结构体数组
/// </summary>
/// <param name="filePath">文件路径</param>
/// <param name="needAging">获取结构体数组</param>
/// <returns></returns>
public int ReadExcelneedToStructs(string filePath, ref NEED_AGING_STRUCT[] needAging)
{
if (string.IsNullOrEmpty(filePath))
{
//无文件路径
return 1;
}
try
{
//创建Excel文件数据流对象
FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
//创建Excel文件对象
IWorkbook workbook = new XSSFWorkbook(fileStream);
//获取第1个表单
ISheet worksheet = workbook.GetSheetAt(0);
//获取单元格值行
int rowCount = worksheet.LastRowNum + 1;
Console.WriteLine("行数=" + rowCount);
if (rowCount <= 1)
{
return 2;//小于两行,无数据内容
}
//按照行数初始化NEED_AGING_STRUCT元素数量
needAging = new NEED_AGING_STRUCT[rowCount - 1];
//获取单元格值列
int colCount = worksheet.GetRow(0).LastCellNum;
Console.WriteLine("列数=" + colCount);
if (colCount != 44)
{
return 3;//列数不正确
}
for (int i = 0; i < rowCount; i++)//行循环
{
Console.Write("第" + i + "行=\n");
IRow row = worksheet.GetRow(i);
string rowString = "";
for (int j = 0; j < colCount; j++)//列循环
{
string cellValue = $"{row.GetCell(j)}";
rowString += cellValue + "\t";
if (i > 0)//非第一行时截取数据
{
switch (j)
{
case 4:
needAging[i - 1].MacAddress = cellValue;
break;
case 6:
needAging[i - 1].AgingResult = cellValue;
break;
case 43:
needAging[i - 1].AgingTime = cellValue;
break;
default:
break;
}
}
}
if (i == 0)
{
if (!rowString.Contains("MAC地址") || !rowString.Contains("老化结束") || !rowString.Contains("老化完成时间"))
{
return 4;//表格首行格式不正确
}
}
Console.WriteLine(rowString);
}
}
catch (Exception ex)
{
Console.WriteLine("ReadExcelFile异常:" + ex.Message);
MessageBox.Show(ex.Message);
}
return 0;
}
(3) 读取EXCEL表格所有需要的数据到列表
- 第三种方法就是一种灵活的方式,通过输入参数来指定或不指定列,最后存到列表list中。
- 建议用户可以在提前检索首行所需要的列,再调用该数,就可以通用不同格式的Excel表格了。
/// <summary>
/// 读取EXCEL表格所有需要的数据到列表
/// </summary>
/// <param name="filePath">路径</param>
/// <param name="colCount">指定列数,小于1则不指定</param>
/// <param name="needColumn">需要的列号,null为不指定</param>
/// <param name="getList">输出list字符串列表</param>
/// <returns>返回空为正常,其它异常</returns>
public string ReadExcelneedToStringList(string filePath, int colCount, int[] needColumn, bool needFirstRow, out List<string> getList)
{
getList = new List<string> { };
if (string.IsNullOrEmpty(filePath))
{
return "无文件路径";
}
try
{
//创建Excel文件数据流对象
FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
//创建Excel文件对象
IWorkbook workbook = new XSSFWorkbook(fileStream);
//获取第1个表单
ISheet worksheet = workbook.GetSheetAt(0);
//获取单元格值行
int rowCount = worksheet.LastRowNum + 1;
Console.WriteLine($"行数={rowCount}");
if (rowCount <= 1)
{
return "小于两行,无数据内容";
}
//获取单元格值列,colCount大于等于1,才检查列数,否则不检查
int getColumnCount = worksheet.GetRow(0).LastCellNum;
Console.WriteLine($"读取列数/确认列数={getColumnCount}/{colCount}");
if (colCount >= 1)
{
if (getColumnCount != colCount)
{
return "列数不正确";
}
}
int firstRowNum = 0;
for (int i = 0; i < rowCount; i++)//行循环
{
Console.Write("第" + i + "行=\n");
IRow row = worksheet.GetRow(i);
string rowString = "", needRowString = "";
//是否需要获取第一行标题行
if (needFirstRow)
firstRowNum = 0;
for (int j = 0; j < getColumnCount; j++)//列循环
{
string cellValue = $"{row.GetCell(j)}";
rowString += cellValue + "\t";
if (i >= firstRowNum)
{
if (needColumn == null || needColumn.Length == 0)
{
needRowString += cellValue + "\t";
}
else
{
foreach (var item in needColumn)
{
//获取需要的列
if (j == item)
{
needRowString += cellValue + "\t";
break;
}
}
}
}
}
Console.WriteLine(rowString);
/* //可以增加第一列判断
if (i == 0)
{
if (!rowString.Contains("IP"))
{
return "表格首行格式不正确";//
}
}*/
if (!string.IsNullOrEmpty(needRowString))
{
getList.Add(needRowString);
}
}
}
catch (Exception ex)
{
string err = "ReadExcelFile异常:" + ex.Message;
return err;
}
return "success";
}
3、写Excel表功能函数设计
写Excel表,即通过已有数据导出Excel表,这里讲2个例子。
(1)从DataGridView 导出新的EXCEL
- 顾名思义,创建新的Excel,需要之前没有文件,是新创建的,所以在fileName定义时,建议添加时间信息尾缀,当然这里也添加了对文件是否存在的检查,大文件尽量使用xlsx。
- 字段名称field_names可以默认也可以自己定义,因为有时候需要使用不同的字段名来展示或者输出给使用者查看,比如输出为英文标题。
- 这里错误信息也提供了英中双语言。
- 格式字体等细节,大家可以依据自己的喜好自行调整选择,但是建议使用大家都有的字体格式,避免不同系统用户出现显示异常。
/// <summary>
/// NPOI DataGridView 导出新的EXCEL (最大行数要减去1行做标题)
/// 03版Excel-xls最大行数是65535行,最大列数是256列
/// 07版Excel-xlsx最大行数是1048575行,最大列数是16384列
/// </summary>
/// <param name="fileName">保存文件名</param>
/// <param name="dataGridView">DataGridView控件</param>
/// <param name="field_names">字段名称--为空则使用表格的默认字段</param>
/// <param name="err_msg">错误消息</param>
/// <param name="msg_in_english">错误消息是否用英文</param>
/// <param name="sheetName">表格名称--有默认值</param>
/// <param name="fontname">字体名称--有默认值</param>
/// <param name="fontsize">字体大小--有默认值</param>
/// <returns>true或false</returns>
public bool ExportExcel(string fileName, DataGridView dataGridView, string[] field_names, out string err_msg, bool msg_in_english, string sheetName = "Sheet1", string fontname = "Tahoma", short fontsize = 10)
{
IWorkbook workbook = default;
ISheet sheet = default;
Stopwatch sw = null;
err_msg = "";
//判断datagridview中内容是否为空
if (dataGridView.Rows.Count == 0)
{
err_msg = msg_in_english ? "The data content is empty!" : "数据内容为空!";
return false;
}
//检测文件是否被占用
if (!IsOccupied(fileName))
{
err_msg = msg_in_english ? $"The file {fileName} is occupied。" : $"文件{fileName}被占用。";
return false;
}
//根据扩展名xls和xlsx来创建对象
string fileExt = Path.GetExtension(fileName).ToLower();
workbook = null;
if (fileExt == ".xlsx")
{
if (dataGridView.Rows.Count > 1048575 || dataGridView.ColumnCount > 16384)
{
err_msg = msg_in_english ? "The number of rows or columns exceeds the specified range" : "DataGridView中行数或列数超出范围(最大行数是 1048575 行,最大列数是 16384 列)!";
return false;
}
else
{
workbook = new XSSFWorkbook();
}
}
else if (fileExt == ".xls")
{
if (dataGridView.Rows.Count > 65535 || dataGridView.ColumnCount > 256)
{
err_msg = msg_in_english ? "The number of rows or columns exceeds the specified range" : "DataGridView中行数或列数超出范围(最大行数是 65535 行,最大列数是 256 列)!\r\n要导出数据,请选择 Excel文件(*.xlsx)";
return false;
}
else
{
workbook = new HSSFWorkbook();
}
}
//创建Sheet
if (workbook != null)
{
sheet = workbook.CreateSheet(sheetName);//Sheet的名称
}
else
{
err_msg = msg_in_english ? "Workbook creation failed!" : "workbook创建失败!";
return false;
}
//程序开始计时
sw = new Stopwatch();
sw.Start();
MemoryStream ms = new MemoryStream(); //MemoryStream
//设置单元格样式
ICellStyle cellStyle = workbook.CreateCellStyle();
//水平居中对齐和垂直居中对齐
cellStyle.Alignment = NPOI.SS.UserModel.HorizontalAlignment.Center;
cellStyle.VerticalAlignment = NPOI.SS.UserModel.VerticalAlignment.Center;
//设置字体
IFont font = workbook.CreateFont();
font.FontName = fontname;//字体名称
font.FontHeightInPoints = fontsize;//字号
font.Color = NPOI.HSSF.Util.HSSFColor.Black.Index;//字体颜色
cellStyle.SetFont(font);
//添加列名
IRow headRow = sheet.CreateRow(0);
if (field_names == null || field_names.Length == 0)
{
//默认使用标题
for (int i = 0; i < dataGridView.Columns.Count; i++)
{
//隐藏行列不导出
if (dataGridView.Columns[i].Visible == true)
{
headRow.CreateCell(i).SetCellValue(dataGridView.Columns[i].HeaderText);
headRow.GetCell(i).CellStyle = cellStyle;
}
}
}
else
{
//自定义标题
for (int i = 0; i < field_names.Length; i++)
{
headRow.CreateCell(i).SetCellValue(field_names[i]);
headRow.GetCell(i).CellStyle = cellStyle;
}
}
//根据类型写入内容
for (int rowNum = 0; rowNum < dataGridView.Rows.Count; rowNum++)
{
///跳过第一行,第一行为列标题
IRow dataRow = sheet.CreateRow(rowNum + 1);
for (int columnNum = 0; columnNum < dataGridView.Columns.Count; columnNum++)
{
int columnWidth = (int)sheet.GetColumnWidth(columnNum) / 256; //列宽
//隐藏行列不导出
if (dataGridView.Rows[rowNum].Visible == true && dataGridView.Columns[columnNum].Visible == true)
{
//防止行列超出Excel限制
if (fileExt == ".xls")
{
//03版Excel最大行数是65535行,最大列数是256列
if (rowNum > 65535)
{
err_msg = msg_in_english ? "The number of rows in the xls file exceeds the limit set by Excel!" : "xls行数超过Excel限制!";
return false;
}
if (columnNum > 256)
{
err_msg = msg_in_english ? "The number of columns in the xls file exceeds the limit set by Excel!" : "xls列数超过Excel限制!";
return false;
}
}
else if (fileExt == ".xlsx")
{
//07版Excel最大行数是1048575行,最大列数是16384列
if (rowNum > 1048575)
{
err_msg = msg_in_english ? "The number of rows in the xlsx file exceeds the limit set by Excel" : "xlsx行数超过Excel限制!";
return false;
}
if (columnNum > 16384)
{
err_msg = msg_in_english ? "The number of columns in the xlsx file exceeds the limit set by Excel" : "xlsx列数超过Excel限制!";
return false;
}
}
ICell cell = dataRow.CreateCell(columnNum);
if (dataGridView.Rows[rowNum].Cells[columnNum].Value == null)
{
cell.SetCellType(CellType.Blank);
}
else
{
cell.SetCellValue(dataGridView.Rows[rowNum].Cells[columnNum].Value.ToString());//所有数据直接按文本输出,简化操作。
}
//设置列宽
IRow currentRow;
if (sheet.GetRow(rowNum) == null)
{
currentRow = sheet.CreateRow(rowNum);
}
else
{
currentRow = sheet.GetRow(rowNum);
}
if (currentRow.GetCell(columnNum) != null)
{
ICell currentCell = currentRow.GetCell(columnNum);
int length = Encoding.Default.GetBytes(currentCell.ToString()).Length;
if (columnWidth < length)
{
columnWidth = length + 10; //设置列宽数值
}
}
sheet.SetColumnWidth(columnNum, columnWidth * 256);
//单元格样式
dataRow.GetCell(columnNum).CellStyle = cellStyle;
//特定单元格红色背景设置为红色背景
if (dataGridView.Rows[rowNum].Cells[columnNum].Style.BackColor == Color.Red || dataGridView.Rows[rowNum].Cells[columnNum].Style.BackColor == Color.Green)
{
currentRow = sheet.GetRow(rowNum + 1);
ICell currentCell = currentRow.GetCell(columnNum);
ICellStyle icellStyle = workbook.CreateCellStyle();
//水平居中对齐和垂直居中对齐
icellStyle.Alignment = NPOI.SS.UserModel.HorizontalAlignment.Center;
icellStyle.VerticalAlignment = NPOI.SS.UserModel.VerticalAlignment.Center;
//设置字体
font.FontName = fontname;//字体名称
font.FontHeightInPoints = fontsize;//字号
font.Color = NPOI.HSSF.Util.HSSFColor.Black.Index;//字体颜色
icellStyle.SetFont(font);
//设置单元格颜色
icellStyle.FillPattern = FillPattern.SolidForeground;
if (dataGridView.Rows[rowNum].Cells[columnNum].Style.BackColor == Color.Red)
{
icellStyle.FillForegroundColor = 10;//红色
}
if (dataGridView.Rows[rowNum].Cells[columnNum].Style.BackColor == Color.Green)
{
icellStyle.FillForegroundColor = 50;//绿色
}
currentCell.CellStyle = icellStyle;//设置
}
}
}
}
//保存为Excel文件
workbook.Write(ms);
FileStream file = new FileStream(fileName, FileMode.Create);
workbook.Write(file);
file.Close();
workbook = null;
ms.Close();
ms.Dispose();
//程序结束计时//
sw.Stop();
double totalTime = sw.ElapsedMilliseconds / 1000.0;
err_msg = msg_in_english ? $"{fileName} export successful,spend {totalTime}s。" : $"{fileName}导出成功,耗时{totalTime}秒。";
return true;
}
(2)将DataGridView导出添加到EXCEL
- 这个例子和上面本质上是有着异曲同工之妙, 不同的是如果文件存在则是在原文件尾继续添加数据,特别适合汇总的Excel表文件。
- 当然新文件可以添加,但新文件需要首行标题的话需要提前添加,可以结合前一个接口程序。
/// <summary>
/// 将DataGridView导出添加到EXCEL(NPOI)
/// </summary>
/// <param name="fileName">保存的文件名</param>
/// <param name="dataGridView">DataGridView数据控件</param>
/// <param name="err_msg">错误消息</param>
/// <param name="msg_in_english">错误消息是否用英文</param>
/// <param name="sheetName">表格名称--有默认值</param>
/// <param name="fontname">字体名称--有默认值</param>
/// <param name="fontsize">字体大小--有默认值</param>
/// <returns>true或false</returns>
public bool ExportAddToExcel(string fileName, DataGridView dataGridView, out string err_msg, bool msg_in_english, string sheetName = "Sheet1", string fontname = "Tahoma", short fontsize = 12)
{
err_msg = string.Empty;
//检测文件是否被占用
if (!IsOccupied(fileName))
{
err_msg = msg_in_english ? $"The file {fileName} is occupied。" : $"文件{fileName}被占用。";
return false;
}
//获取扩展名xls和xlsx
string fileExt = Path.GetExtension(fileName).ToLower();
// 打开现有的Excel文件,读取文件流
FileStream file = new FileStream(fileName, FileMode.Open, FileAccess.ReadWrite);
IWorkbook workbook = new XSSFWorkbook(file);
ISheet sheet;
if (string.IsNullOrEmpty(sheetName))
sheet = workbook.GetSheetAt(0); //使用workbook.GetSheetAt(0)获取第一个工作表
else
sheet = workbook.GetSheet(sheetName); //指定工作表
// 检查sheet是否为空,如果不存在则创建新的sheet
if (sheet == null)
{
sheet = workbook.CreateSheet(sheetName);
}
MemoryStream ms = new MemoryStream(); //MemoryStream
// 创建单元格样式
NPOI.SS.UserModel.ICellStyle cellStyle = workbook.CreateCellStyle();
cellStyle.BorderTop = NPOI.SS.UserModel.BorderStyle.None;
cellStyle.BorderRight = NPOI.SS.UserModel.BorderStyle.None;
cellStyle.BorderBottom = NPOI.SS.UserModel.BorderStyle.None;
cellStyle.BorderLeft = NPOI.SS.UserModel.BorderStyle.None;
cellStyle.Alignment = NPOI.SS.UserModel.HorizontalAlignment.Center;
cellStyle.VerticalAlignment = NPOI.SS.UserModel.VerticalAlignment.Center;
//设置字体
IFont font = workbook.CreateFont();
font.FontName = fontname;//字体名称
font.FontHeightInPoints = fontsize;//字号
font.Color = NPOI.HSSF.Util.HSSFColor.Black.Index;//字体颜色
cellStyle.SetFont(font);
int last_row_num = sheet.LastRowNum; // 获取尾行的行号
//根据类型写入内容
for (int rowNum = 0; rowNum < dataGridView.Rows.Count; rowNum++)
{
IRow row = sheet.CreateRow(++last_row_num); // 创建新行,行号+1
for (int columnNum = 0; columnNum < dataGridView.Columns.Count; columnNum++)
{
//隐藏行列不导出
if (dataGridView.Rows[rowNum].Visible == true && dataGridView.Columns[columnNum].Visible == true)
{
//防止行列超出Excel限制
if (fileExt == ".xls")
{
//03版Excel最大行数是65535行,最大列数是256列
if (rowNum > 65535)
{
err_msg = msg_in_english ? "The number of rows in the xls file exceeds the limit set by Excel!" : "xls行数超过Excel限制!";
return false;
}
if (columnNum > 256)
{
err_msg = msg_in_english ? "The number of columns in the xls file exceeds the limit set by Excel!" : "xls列数超过Excel限制!";
return false;
}
}
else if (fileExt == ".xlsx")
{
//07版Excel最大行数是1048575行,最大列数是16384列
if (rowNum > 1048575)
{
err_msg = msg_in_english ? "The number of rows in the xlsx file exceeds the limit set by Excel" : "xlsx行数超过Excel限制!";
return false;
}
if (columnNum > 16384)
{
err_msg = msg_in_english ? "The number of columns in the xlsx file exceeds the limit set by Excel" : "xlsx列数超过Excel限制!";
return false;
}
}
//写入数据
//row.CreateCell(0).SetCellValue(2); //row.CreateCell(1).SetCellValue("Charlie");
if (dataGridView.Rows[rowNum].Cells[columnNum].Value == null)
{
row.CreateCell(columnNum).SetCellType(CellType.Blank);
}
else
{
row.CreateCell(columnNum).SetCellValue(dataGridView.Rows[rowNum].Cells[columnNum].Value.ToString());//所有数据直接按文本输出
}
//单元格样式
row.GetCell(columnNum).CellStyle = cellStyle;
}
}
}
// 保存更改
using (FileStream fileOut = new FileStream(fileName, FileMode.Open, FileAccess.Write))
{
workbook.Write(fileOut);
}
ms.Close();
ms.Dispose();
workbook.Close();
// workbook资源释放
file.Close();
return true;
}
(3)引用系统API检测文件是否被占用
- 在保存文件的时候经常需要用到
#region 检测文件是否被占用
// 判断文件是否打开
[DllImport("kernel32.dll")]
public static extern IntPtr _lopen(string lpPathName, int iReadWrite);
// 关闭文件句柄
[DllImport("kernel32.dll")]
public static extern bool CloseHandle(IntPtr hObject);
// 常量
public const int OF_READWRITE = 2;
public const int OF_SHARE_DENY_NONE = 0x40;
public static readonly IntPtr HFILE_ERROR = new IntPtr(-1);
private bool IsOccupied(string filePath)
{
if (!File.Exists(filePath))
{
//文件不存在
return true;
}
IntPtr vHandle = _lopen(filePath, OF_READWRITE | OF_SHARE_DENY_NONE);
if (vHandle == HFILE_ERROR)
{
//文件被占用
return false;
}
//文件没被占用
CloseHandle(vHandle);
return true;
}
#endregion //检测文件是否被占用
总结
本文通过NPIO接口C#语言描述了3个Excel导入的函数和2个导出的行数,各有千秋,大同小异。朋友们可以自己研究,看看有没有比NPOI更加好的方案来控制Excel的读写,可以告诉我。
以上就是C#通过NPIO读写Excel表的方法详解的详细内容,更多关于C# NPIO读写Excel表的资料请关注脚本之家其它相关文章!
