添加依赖库
通过 NuGet 添加:
代码
using NPOI.HSSF.UserModel;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
namespace netframeword46_WindowsFormsApp1.docutil
{
public class NPOIExcelUtil
{
FileStream stream = null;
IWorkbook workbook = null;
ISheet worksheet = null;
string file;
public void Open(string file)
{
this.file = file;
string extension = Path.GetExtension(file).ToLower();
stream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
if (extension == ".xls")
{
workbook = new HSSFWorkbook(stream); // .xls格式
}
else if (extension == ".xlsx")
{
workbook = new XSSFWorkbook(stream); // .xlsx格式
}
}
public IList<string> GetWorksheets()
{
IList<string> worksheets = new List<string>();
// 遍历所有工作表
for (int i = 0; i < workbook.NumberOfSheets; i++)
{
ISheet sheet = workbook.GetSheetAt(i);
if (sheet == null) continue;
// 获取工作表基本信息
string sheetName = sheet.SheetName;
worksheets.Add(sheetName);
}
return worksheets;
}
/// <summary>
/// 选用sheet
/// </summary>
/// <param name="sheetIndex">从0开始</param>
public void UseSheet(int sheetIndex)
{
worksheet = workbook.GetSheetAt(sheetIndex);
}
public DataSet ReadAllWorkbook()
{
System.Data.DataSet dataSet = new System.Data.DataSet();
for (int sheetIndex = 0; sheetIndex < workbook.NumberOfSheets; sheetIndex++)
{
ISheet worksheet = workbook.GetSheetAt(sheetIndex);
System.Data.DataTable dataTable = new System.Data.DataTable(worksheet.SheetName);
dataSet.Tables.Add(dataTable);
UseSheet(sheetIndex);
// 获取工作表的最大列数(用于创建DataTable列)
int maxColumnCount = GetMaxColumnCount();
if (maxColumnCount <= 0)
{
continue; // 无数据列,跳过该表
}
// 创建DataTable列(使用默认列名:Column1, Column2...)
for (int col = 0; col < maxColumnCount; col++)
{
dataTable.Columns.Add($"Column{col + 1}");
}
// 读取所有行数据(从第0行开始,所有行均为数据行)
for (int rowIndex = worksheet.FirstRowNum; rowIndex <= worksheet.LastRowNum; rowIndex++)
{
IRow dataRow = worksheet.GetRow(rowIndex);
if (dataRow == null) continue;
DataRow dtRow = dataTable.NewRow();
bool hasData = false; // 标记行是否有有效数据
// 填充单元格数据
for (int col = 0; col < maxColumnCount; col++)
{
// 允许单元格不存在(返回空)
ICell cell = dataRow.GetCell(col, MissingCellPolicy.RETURN_BLANK_AS_NULL);
object cellValue = GetCellValueByType(cell);
if (cellValue!=null)
{
hasData = true;
}
dtRow[col] = cellValue ?? DBNull.Value;
}
// 只添加有数据的行
if (hasData)
{
dataTable.Rows.Add(dtRow);
}
}
}
return dataSet;
}
/// <summary>
/// 获取工作表的最大列数(用于确定DataTable的列数)
/// </summary>
public int GetMaxColumnCount()
{
int maxColumns = 0;
for (int rowIndex = worksheet.FirstRowNum; rowIndex <= worksheet.LastRowNum; rowIndex++)
{
IRow row = worksheet.GetRow(rowIndex);
if (row != null && row.LastCellNum > maxColumns)
{
maxColumns = row.LastCellNum;
}
}
return maxColumns;
}
/// <summary>
///
/// </summary>
/// <param name="sheet"></param>
/// <param name="column">列名是A、B、C</param>
/// <param name="rowStart">行索引从1开始</param>
/// <param name="rowEnd">行索引从1开始</param>
/// <returns></returns>
public System.Data.DataTable Read(int sheet, string column, int rowStart, int rowEnd)
{
return Read(sheet, new string[] { column }, rowStart, rowEnd);
}
/// <summary>
///
/// </summary>
/// <param name="sheetIndex"></param>
/// <param name="columns">列名是A、B、C</param>
/// <param name="rowStart">行索引从1开始</param>
/// <param name="rowEnd">行索引从1开始</param>
/// <returns></returns>
public System.Data.DataTable Read(int sheetIndex, string[] columns, int rowStart, int rowEnd)
{
rowStart--;// npoi第一行索引是0,为了符合excel行号从1开始,所以-1
rowEnd--;// npoi第一行索引是0,为了符合excel行号从1开始,所以-1
ISheet worksheet = workbook.GetSheetAt(sheetIndex);
System.Data.DataTable dataTable = new System.Data.DataTable(worksheet.SheetName);
foreach (string item in columns)
{
dataTable.Columns.Add(item);
}
// 遍历每一行每一列
for (int rowIndex = rowStart; rowIndex <= rowEnd; rowIndex++)
{
var newRow = dataTable.NewRow();
dataTable.Rows.Add(newRow);
for (int i = 0; i < columns.Length; i++)
{
int columnIndex = GetColumnIndexByName(columns[i]);
ICell cell = worksheet.GetRow(rowIndex).GetCell(columnIndex);
var cellValue = GetCellValueByType(cell);
// 去掉左右空格,在根据列比对是否相等时,因为空格导致查询不到
//newRow[i] = cellValue?.ToString().Trim() ?? "";
//newRow[columnIndex] = cellValue?.ToString() ?? "";
newRow[i] = cellValue;
}
}
return dataTable;
}
/// <summary>
/// 获取单元格类型
/// </summary>
/// <param name="cell">目标单元格</param>
/// <returns></returns>
private object GetCellValueByType(ICell cell)
{
if (cell == null)
return null;
switch (cell.CellType)
{
case CellType.Blank:
return null;
case CellType.Boolean:
return cell.BooleanCellValue;
case CellType.Numeric:
return cell.NumericCellValue;
case CellType.String:
return cell.StringCellValue;
case CellType.Error:
return cell.ErrorCellValue;
case CellType.Formula:
default:
return "=" + cell.CellFormula;
}
}
/// <summary>
///
/// </summary>
/// <param name="sheet"></param>
/// <param name="columns">列名是A、B、C</param>
/// <param name="rowStart">行索引从1开始</param>
/// <param name="rowEnd">行索引从1开始</param>
/// <param name="datas"></param>
public void Write(int sheet, string[] columns, int rowStart, int rowEnd, IList<object[]> datas)
{
rowStart--;// npoi第一行索引是0,为了符合excel行号从1开始,所以-1
rowEnd--;// npoi第一行索引是0,为了符合excel行号从1开始,所以-1
ISheet worksheet = workbook.GetSheetAt(sheet);
int rowIndexOfDatas = 0;
// 遍历每一行每一列
for (int rowIndex = rowStart; rowIndex <= rowEnd; rowIndex++)
{
IRow row = worksheet.CreateRow(rowIndex);
object[] rowOfDatas = datas[rowIndexOfDatas];
rowIndexOfDatas++;
//int colIndexOfDatas = 0;
for (int i = 0; i < columns.Length; i++)
{
int colIndex = GetColumnIndexByName(columns[i]);
ICell cell = row.CreateCell(colIndex);
SetCellValue(cell,rowOfDatas[i]);
//worksheet.Cells[rowIndex, columns[colIndex]] = rowOfDatas[colIndex];
//colIndexOfDatas++;
}
}
}
/// <summary>
/// 根据数据类型设置单元格值
/// </summary>
private static void SetCellValue(ICell cell, object value)
{
if (value == null)
{
cell.SetCellValue("");
return;
}
// 根据值的类型设置单元格数据类型
switch (value)
{
case string str:
cell.SetCellValue(str);
break;
case int num:
cell.SetCellValue(num);
break;
case double num:
cell.SetCellValue(num);
break;
case decimal num:
cell.SetCellValue((double)num);
break;
case DateTime date:
cell.SetCellValue(date);
break;
case bool b:
cell.SetCellValue(b);
break;
default:
// 其他类型转为字符串
cell.SetCellValue(value.ToString());
break;
}
}
/// <summary>
///
/// </summary>
/// <param name="sheet"></param>
/// <param name="column">列名是A、B、C</param>
/// <param name="rowStart">行索引从1开始</param>
/// <param name="rowEnd">行索引从1开始</param>
/// <param name="datas"></param>
public void Write(int sheet, string column, int rowStart, int rowEnd, object[] datas)
{
IList<object[]> list = new List<object[]>() { datas };
Write(sheet, new string[] { column }, rowStart, rowEnd, list);
}
/// <summary>
///
/// </summary>
/// <param name="sheet"></param>
/// <param name="column">列名是A、B、C</param>
/// <param name="rowIndex">行索引从1开始</param>
/// <param name="data"></param>
public void Write(int sheet, string column, int rowIndex, object data)
{
rowIndex--;// npoi第一行索引是0,为了符合excel行号从1开始,所以-1
int colIndex = GetColumnIndexByName(column);
ISheet worksheet = workbook.GetSheetAt(sheet);
ICell cell = worksheet.GetRow(rowIndex).GetCell(colIndex);
//IRow row = worksheet.CreateRow(rowIndex);
//ICell cell = row.CreateCell(colIndex);
// 设置单元格值(根据数据类型处理)
SetCellValue(cell, data);
}
public void Create(string file)
{
string extension = Path.GetExtension(file).ToLower();
stream = new FileStream(file, FileMode.Create, FileAccess.Write);
if (extension == ".xls")
{
workbook = new HSSFWorkbook(stream); // .xls格式
}
else if (extension == ".xlsx")
{
workbook = new XSSFWorkbook(stream); // .xlsx格式
}
}
public void Save()
{
Save(file);
}
public void Save(string file)
{
using (FileStream fileStream = new FileStream(file, FileMode.Create, FileAccess.Write))
{
workbook.Write(fileStream);
}
//workbook.Write(stream);
}
public void Close()
{
if(workbook!=null)workbook.Close();
if(stream!=null)stream.Close();
}
/// <summary>
/// 将单个字符的字符串(A-Z)转换为对应数字(1-26)
/// </summary>
private static int GetColumnIndexByName(string input)
{
if (string.IsNullOrEmpty(input) || input.Length != 1)
{
throw new ArgumentException("输入必须是单个字符的字符串");
}
char letter = char.ToUpper(input[0]);
if (!char.IsLetter(letter))
{
throw new ArgumentException("输入必须是字母A-Z");
}
return letter - 'A';
}
}
}
