C# 【通过NPIO读写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的几个引用

c 复制代码
 //工具 → "管理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表个内容,表单使用的默认的第一个表单,当然你也可以以名称的方式获取表单。
  • 它是通过首行和首列来获取行数和列数的,所以原始文件不要有首行首列中间为空的情况。
c 复制代码
/// <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表格所有数据到字符串数组

  • 同上面的方法大统小异,不同的是增加了部分限制条件,一方面,既定义了获取文件行列数的限制,也规定了只获取表中指定列的内容,适合在大表格中获取部分需要的数据。另一方面,我们固定获取指定位置的数据,转到数据结构,需要用户提供的数据列标题和内容必须严格对应,不然就会获取错误。
c 复制代码
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表格了。
c 复制代码
/// <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";
}

2、写Excel表功能函数设计

写Excel表,即通过已有数据导出Excel表,这里讲2个例子。

(1)从DataGridView 导出新的EXCEL

  • 顾名思义,创建新的Excel,需要之前没有文件,是新创建的,所以在fileName定义时,建议添加时间信息尾缀,当然这里也添加了对文件是否存在的检查,大文件尽量使用xlsx。
  • 字段名称field_names可以默认也可以自己定义,因为有时候需要使用不同的字段名来展示或者输出给使用者查看,比如输出为英文标题。
  • 这里错误信息也提供了英中双语言。
  • 格式字体等细节,大家可以依据自己的喜好自行调整选择,但是建议使用大家都有的字体格式,避免不同系统用户出现显示异常。
c 复制代码
/// <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表文件。
  • 当然新文件可以添加,但新文件需要首行标题的话需要提前添加,可以结合前一个接口程序。
c 复制代码
/// <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检测文件是否被占用

  • 在保存文件的时候经常需要用到
c 复制代码
 #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的读写,可以告诉我。

相关推荐
葡萄城技术团队2 小时前
Excel公式前的“@”符号:是Bug还是黑科技?
科技·bug·excel
爱折磨键盘的大鹏2 小时前
若依框架实现Excel动态下拉(查库)
excel
LF男男3 小时前
MK - Grand Mahjong Game-
unity·c#
代数狂人3 小时前
《深入浅出Godot 4与C# 3D游戏开发》第一章:了解Godot与搭建开发环境
c#·游戏引擎·godot
齐鲁大虾17 小时前
新人编程语言选择指南
javascript·c++·python·c#
加号317 小时前
【C#】 WebAPI 接口设计与实现指南
开发语言·c#
unicrom_深圳市由你创科技18 小时前
上位机开发常用的语言 / 框架有哪些?
c++·python·c#
xiaoshuaishuai820 小时前
C# ZLibrary数字资源分发
开发语言·windows·c#
Omics Pro21 小时前
华大等NC|微生物多样性与抗菌物质发现
大数据·人工智能·深度学习·语言模型·excel