NPOI之建,写,合并

C#winform利用NPOI对xls文件的操作一

Posted by ZXZ on July 2, 2019

以下参考简书上面的作者银冰雪千载 以及CSDN上面的作者Icoding_F2014

NPOI的介绍

什么是NPOI

NPOI 是指构建在 POI 3.x 版本之上的一个程序,NPOI 可以在没有安装 Office 的情况下对 Word 或 Excel 文档进行读写操作。

为什么使用NPOI

比较常用的操作 Excel 的方法如下所示:

1 OLEDB 这种方式是把整个 Excel 文件当做一个数据源来进行数据的读取操作。 优点:实现方式简单,读取速度快。 缺点:读取 Excel 数据的过程不太灵活,对内存的占用比较高,当数据量变的很大时,容易由于内存空间不足导致内存溢出异常。

2 COM 组件 这种方式是通过 COM 组件 Microsoft.Office.Interop.Excel.dll 实现 Excel 文件的操作。 优点:读取 Excel 数据非常灵活,可以实现 Excel 具有的各种数据处理功能。 缺点:对数据的访问时基于单元格方式实现的,所以读写数据较慢,特别是当数据量较大时,访问效率问题更为突出。另一点是要求本机安装了 Office 组件。

3 NPOI 这种方式是通过 NPOI 库实现 Excel 文件操作,可以在没有安装微软 Office 的情况下使用。详情可查看官网。 优点:读取Excel数据速度较快,操作方式灵活。 缺点:官方文档较少。

4 ASPOSE Aspose 公司旗下的最全的一套 office 文档管理方案。支持 Word, Excel, PowerPoint, Project 等 office 文档以及 PDF 文档读写操作。 优点:因商业驱动所以会有详细的使用文档和技术支持。 缺点:需要收费使用。

这里推荐使用 NPOI,因为 NPOI 不用额外安装 Office 组件,而且完全免费。接下来介绍如何使用 NPOI 。

编程开发思想

不管是读还是写一个excel文件,都要先生成一个HSSFWorkbook对象。 NPOI里面的管理层次为:workbook->worksheet->row->cell. 类比关系型数据库就是:

NPOI 关系型数据库
workbook database
worksheet table表
row record记录
cell field字段

形象的表达如图 img

使用

针对昨天做的东西的理解的记录

废话不多说先上完整代码

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using NPOI.HSSF.UserModel;
using NPOI.SS.UserModel;
using NPOI.SS.Util;

namespace NPOI_Test_1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }

        private void btn_Test_Click(object sender, EventArgs e)
        {
            //创建一个新的Excel文件的workbook对象
            HSSFWorkbook wb = new HSSFWorkbook();
            //FileStream file;
            //string filepath = @"D:\测试NPOI.xls";
            //file =new FileStream(filepath,FileMode.Open,FileAccess.Read);
            //wb=new HSSFWorkbook(file);
            //file.Close();
            //在当前wb中创建一个名为sheet1的新的sheet页
            //ISheet sheet = new ISheet();
           // HSSFSheet sheet = new HSSFSheet();
            ISheet sheet = wb.CreateSheet("Sheet1");
            int num = 0;
            for (int i = 0; i < 4; i++)
            {
                sheet.CreateRow(i);//创建i行
                for (int j = 0; j < 4; j++)
                {
                    ICell cell1 = sheet.GetRow(i).CreateCell(j);//在i某一行创建j列
//                     cell1.SetCellValue(num);
//                     num++;
                }
            }

            for (int i = 0; i < 4; i++)
            {
                for (int j = 0; j < 4; j++)
                {
                    ICell cell1 = sheet.GetRow(i).GetCell(j);
                    cell1.SetCellValue(num);
                    num++;
                }
            }
            sheet.AddMergedRegion(new CellRangeAddress(0, 0, 0, 1));
            //D:\\测试NPOI_1.xls
            FileStream sw = File.Create("D:\\测试NPOI_5.xls");
           // file = new FileStream(filepath, FileMode.Open, FileAccess.Write);
            wb.Write(sw);
            sw.Close();  //资源释放
            wb.Close();

        }
    }
}


引入库

img

如图所示这些库引入后NPOI的操作都可以完成了

新建/打开

    // 新建一个xls文件
    //创建一个新的Excel文件的workbook对象
    HSSFWorkbook wb = new HSSFWorkbook();

    //保存路径及名称为 D:\\测试NPOI_1.xls
    FileStream sw = File.Create("D:\\测试NPOI_5.xls");
    //把wb写进sw文件中去
    wb.Write(sw);
    //资源释放  当时粗心把这个注释掉了 出现了程序跑起来之后创建了一个xls文件但是如果不把程序关了就打不开这个文件
    sw.Close();
    wb.Close();

    //打开一个已有的xls文件
    HSSFWorkbook wb;
    FileStream file;
    file = new FileStream(filepath, FileMode.Open, FileAccess.Read);
    wb = new HSSFWorkbook(file);
    file.Close();

创建/打开 sheet页


    //创建一个新的名为SheetA的sheet页
    ISheet sheet = wb.CreateSheet("SheetA");
    //打开一个已有的名为SheetA的sheet页
    ISheet sheet;
    sheet = wb.GetSheet("SheetA")

获得当前文件中所有sheet页


    List<ISheet> sheets = new List<ISheet>();
            ISheet sheet = null;
            int sheetCount = wb.NumberOfSheets;
            for (int i = 0; i < sheetCount; i++)
            {
                sheet = wb.GetSheetAt(i);
                if (sheet.LastRowNum > 0)
                {
                    sheets.Add(sheet);
                }
            }

创建行和列

    
    //创建一个 4*4 的表格
    for (int i = 0; i < 4; i++)
    {
        sheet.CreateRow(i);//创建i行
        for (int j = 0; j < 4; j++)
        {
            sheet.GetRow(i).CreateCell(j);//在i某一行创建j列
        }
    }

写入内容


    //在创建好的 4*4 表格中填写内同
    for (int i = 0; i < 4; i++)
    {
        for (int j = 0; j < 4; j++)
        {
            //IRow
            ICell cell1 = sheet.GetRow(i).GetCell(j);
            cell1.SetCellValue(num);
            num++;
        }
    }

合并单元格


    //合并第一行的第一列到第二列
    sheet.AddMergedRegion(new CellRangeAddress(0, 0, 0, 1));
    //表格的行或者列都是从0开始
    //参数的意义 从第0行开始到第0行结束 从第0列开始到第1列结束

判断当前单元格是否在合并单元格内


       /// <summary>
        /// 判断是否在和并单元格中
        /// </summary>
        /// <param name="sheet">sheet表单</param>
        /// <param name="rowIndex">行索引 0开始</param>
        /// <param name="colIndex">列索引 0开始</param>
        /// <param name="start">合并单元格左上角坐标</param>
        /// <param name="end">合并单元格右下角坐标</param>
        /// <returns>返回false表示非合并单元格</returns>
        private static bool IsMergeCell(ISheet sheet, int rowIndex, int colIndex)
        {
            bool result = false;
            Point start = new Point(0, 0);
            Point end = new Point(0, 0);
            if ((rowIndex < 0) || (colIndex < 0)) return result;
            int regionsCount = sheet.NumMergedRegions;
            for (int i = 0; i < regionsCount; i++)
            {
                CellRangeAddress range = sheet.GetMergedRegion(i);
                //sheet.IsMergedRegion(range); 
                if (rowIndex >= range.FirstRow && rowIndex <= range.LastRow && colIndex >= range.FirstColumn && colIndex <= range.LastColumn)
                {
                    start = new Point(range.FirstRow, range.FirstColumn);
                    end = new Point(range.LastRow, range.LastColumn);
                    result = true;
                    break;
                }
            }
            return result;
        }

根据当前单元格获取所在的合并单元格的值


        /// <summary>
        /// 获取合并单元格的值
        /// </summary>
        /// <param name="sheet">第一个sheet</param>
        /// <param name="row">初始行</param>
        /// <param name="column">初始列</param>
        /// <returns></returns>
        private static object GetMergedRegionStr(ISheet sheet, int row, int column)
        {
            //初始化坐标
            Point start = new Point(0, 0);
            Point end = new Point(0, 0);
            //获取合并单元格的数量
            int regionsCount = sheet.NumMergedRegions;
            //初始化单元格的值
            object cellValue = new object();
            //判断坐标
            for (int j = 0; j < regionsCount; j++)
            {
                //获得不同坐标
                CellRangeAddress range = sheet.GetMergedRegion(j);
                //判读参数坐标是否存在
                if (row >= range.FirstRow && row <= range.LastRow && column >= range.FirstColumn && column <= range.LastColumn)
                {
                    //获取坐标
                    start = new Point(range.FirstRow, range.FirstColumn);
                    end = new Point(range.LastRow, range.LastColumn);
                    //坐标x=row,y=colum
                    int x = start.X;//获取所在坐标行号
                    int y = start.Y;//获取所在坐标列号
                    //获取所在行
                    IRow fRow = sheet.GetRow(x);
                    //获取所在列
                    ICell fCell = fRow.GetCell(y);
                    //判断cell的类型
                    if (fCell.CellType == CellType.String)//string类型
                    {
                        cellValue = fCell.StringCellValue;
                    }
                    else if (fCell.CellType == CellType.Numeric)//数字类型
                    {
                        //判断是否是时间类型
                        if (HSSFDateUtil.IsCellDateFormatted(fCell))
                        {
                            cellValue = fCell.DateCellValue;
                        }
                        else
                        {
                            cellValue = fCell.NumericCellValue;
                        }
 
                    }
                    else if (fCell.CellType == CellType.Formula)//单元格有公式类型
                    {
                        cellValue = fCell.NumericCellValue;
                    }
                    else
                    {
                        cellValue = "";
                    }
                    break;
                }
            }
            return cellValue;
        }

NPOI 自定义格式

遇到自定义格式的数据想要获得值的时候需要提前获得它的自定义格式,然后把获得的值通过获得的自定义格式转换成想要需要的格式。

img icell.ToString() 获得的是 C2+E2 ,如果遇到自定义格式得数据 得到的是没有格式得数据怎么办,比如想要获得DIK30+565 首先获取当前单元格的自定义格式,比如: string formatstring1 = ice1.CellStyle.GetDataFormatString(); 获得的就是这样的字符串 直接获得下图字符串 img 然后把获得的值通过获得的自定义格式转换成想要需要的格式 NumericCellValue(给当前单元格数据设置格式) string value1 = ice1.NumericCellValue.ToString(formatstring1); DIK30+565