C#通過NPIO讀寫Excel表的方法詳解
前言
近年一些老舊工程的發(fā)現(xiàn)會(huì)因?yàn)楣こ绦枰褂貌僮鱁xcel表調(diào)用office的接口(Microsoft.Office.Interop.Excel接口),在部分電腦中會(huì)出現(xiàn)無法啟動(dòng)工程、報(bào)COM錯(cuò)誤或者保存文件時(shí)偶爾閃退等異常。這個(gè)問題對應(yīng)使用方確實(shí)是有些痛苦,排查起來也比較困難,可能是部分盜版系統(tǒng)不完整也可能是office版本,也可能是系統(tǒng)設(shè)置問題,如果工程代碼可已重構(gòu)和替換接口,則建議使用NPOI來實(shí)現(xiàn),要穩(wěn)定很多。本文就NPOI的優(yōu)點(diǎn)以及如何使用它讀寫Excel表,并使用代碼描述給大家參考。
Microsoft.Office.Interop.Excel和NPOI的對比
如下表多項(xiàng)對比,可以看出NPOI很是有著較大的優(yōu)勢,大家在讀寫控制Excel表格時(shí),建議優(yōu)先考慮。
| 對比項(xiàng) | Microsoft.Office.Interop.Excel | NPOI |
|---|---|---|
| 環(huán)境依賴 | 必須安裝 Office,Windows 獨(dú)占 | 無 Office 依賴,跨平臺(tái)(Windows/Linux) |
| 性能與內(nèi)存 | 慢、耗內(nèi)存、易泄漏;大數(shù)據(jù)易卡死 | 快、輕量、穩(wěn)定;大數(shù)據(jù)更友好 |
| 并發(fā) / 服務(wù)器 | 極差:單線程、進(jìn)程殘留、權(quán)限問題 | 優(yōu)秀:多線程安全、純內(nèi)存 / 流操作 |
| 部署難度 | 高:需安裝 Office、配置 DCOM、權(quán)限 | 低:NuGet 安裝,直接部署 |
| 功能完整度 | 最全:VBA、宏、圖表、透視表、函數(shù)等 | 主流功能:樣式、公式、圖表、合并單元格等 |
| 版本兼容 | 易出現(xiàn)版本沖突(2016/2019/365) | 統(tǒng)一 API,無版本問題 |
| 授權(quán) | 需 Office 授權(quán)(服務(wù)器商用需額外許可) | 開源免費(fèi)(Apache 2.0) |
| 適用場景 | Windows 桌面、單機(jī)自動(dòng)化、需完整 Excel 特性 | 服務(wù)器導(dǎo)出 / 導(dǎo)入、Web、跨平臺(tái)、大數(shù)據(jù) |
| 常見坑 | 進(jìn)程殺不掉、文件占用、權(quán)限、COM 異常 | 部分高級(jí)格式 / 函數(shù)需手動(dòng)處理 |
這里使用VS2022下基于.netFramework4.8.1工程來描述NPOI的部分功能。提示: 下面案例可供參考
一、添加第三方庫
在根據(jù)的net包管理器中找到管理解決方案的netGet程序包(如圖所示)。

在瀏覽中搜索"NPOI”,可以找到人氣最旺的NPIO,可以安裝最新版本,點(diǎn)擊安裝即可。我這里安裝了穩(wěn)定版本2.7.6,已確認(rèn)該版本可以使用。

- 安裝成功后可以看到NuGet解決方案中已安裝中就有了這個(gè)NPOI項(xiàng)目,并可以點(diǎn)擊卸載。
- 在工程解決方案資源管理器的引用中也可以看到添加的NPOI項(xiàng)目。

二、代碼描述構(gòu)建函數(shù)
在工程文件中添加一個(gè) ExcelOperation.cs的類文件,就可以開始添加對應(yīng)的接口代碼了。
1、添加引用
除了系統(tǒng)使用的其他引用務(wù)必加上NPOI的幾個(gè)引用
//工具 → “管理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表功能函數(shù)設(shè)計(jì)
(1) 讀取EXCEL表格所有數(shù)據(jù)到字符串?dāng)?shù)組
- 可以看到這里通過數(shù)據(jù)流的方式出處Excel表個(gè)內(nèi)容,表單使用的默認(rèn)的第一個(gè)表單,當(dāng)然你也可以以名稱的方式獲取表單。
- 它是通過首行和首列來獲取行數(shù)和列數(shù)的,所以原始文件不要有首行首列中間為空的情況。
/// <summary>
/// 讀取EXCEL表格所有數(shù)據(jù)到字符串?dāng)?shù)組
/// </summary>
/// <param name="filePath">文件路徑</param>
/// <returns></returns>
public string[] ReadExcelAllToStrs(string filePath)
{
//初始化返回值
string[] strs = new string[] { };
try
{
//創(chuàng)建Excel文件數(shù)據(jù)流對象
FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
//創(chuàng)建Excel文件對象
IWorkbook workbook = new XSSFWorkbook(fileStream);
//獲取第1個(gè)表單
ISheet worksheet = workbook.GetSheetAt(0);
//獲取單元格值行列
int rowCount = worksheet.LastRowNum + 1;
Console.WriteLine("行數(shù)=" + rowCount);
//按照行數(shù)初始化strs元素?cái)?shù)量
strs = new string[rowCount];
int colCount = worksheet.GetRow(0).LastCellNum;
Console.WriteLine("列數(shù)=" + 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表格所有數(shù)據(jù)到字符串?dāng)?shù)組
- 同上面的方法大統(tǒng)小異,不同的是增加了部分限制條件,一方面,既定義了獲取文件行列數(shù)的限制,也規(guī)定了只獲取表中指定列的內(nèi)容,適合在大表格中獲取部分需要的數(shù)據(jù)。另一方面,我們固定獲取指定位置的數(shù)據(jù),轉(zhuǎn)到數(shù)據(jù)結(jié)構(gòu),需要用戶提供的數(shù)據(jù)列標(biāo)題和內(nèi)容必須嚴(yán)格對應(yīng),不然就會(huì)獲取錯(cuò)誤。
struct NEED_AGING_STRUCT
{
public string MacAddress;
public string AgingResult;
public string AgingTime;
}
/// <summary>
/// 讀取EXCEL表格所有需要的數(shù)據(jù)到指定的結(jié)構(gòu)體數(shù)組
/// </summary>
/// <param name="filePath">文件路徑</param>
/// <param name="needAging">獲取結(jié)構(gòu)體數(shù)組</param>
/// <returns></returns>
public int ReadExcelneedToStructs(string filePath, ref NEED_AGING_STRUCT[] needAging)
{
if (string.IsNullOrEmpty(filePath))
{
//無文件路徑
return 1;
}
try
{
//創(chuàng)建Excel文件數(shù)據(jù)流對象
FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
//創(chuàng)建Excel文件對象
IWorkbook workbook = new XSSFWorkbook(fileStream);
//獲取第1個(gè)表單
ISheet worksheet = workbook.GetSheetAt(0);
//獲取單元格值行
int rowCount = worksheet.LastRowNum + 1;
Console.WriteLine("行數(shù)=" + rowCount);
if (rowCount <= 1)
{
return 2;//小于兩行,無數(shù)據(jù)內(nèi)容
}
//按照行數(shù)初始化NEED_AGING_STRUCT元素?cái)?shù)量
needAging = new NEED_AGING_STRUCT[rowCount - 1];
//獲取單元格值列
int colCount = worksheet.GetRow(0).LastCellNum;
Console.WriteLine("列數(shù)=" + colCount);
if (colCount != 44)
{
return 3;//列數(shù)不正確
}
for (int i = 0; i < rowCount; i++)//行循環(huán)
{
Console.Write("第" + i + "行=\n");
IRow row = worksheet.GetRow(i);
string rowString = "";
for (int j = 0; j < colCount; j++)//列循環(huán)
{
string cellValue = $"{row.GetCell(j)}";
rowString += cellValue + "\t";
if (i > 0)//非第一行時(shí)截取數(shù)據(jù)
{
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("老化結(jié)束") || !rowString.Contains("老化完成時(shí)間"))
{
return 4;//表格首行格式不正確
}
}
Console.WriteLine(rowString);
}
}
catch (Exception ex)
{
Console.WriteLine("ReadExcelFile異常:" + ex.Message);
MessageBox.Show(ex.Message);
}
return 0;
}
(3) 讀取EXCEL表格所有需要的數(shù)據(jù)到列表
- 第三種方法就是一種靈活的方式,通過輸入?yún)?shù)來指定或不指定列,最后存到列表list中。
- 建議用戶可以在提前檢索首行所需要的列,再調(diào)用該數(shù),就可以通用不同格式的Excel表格了。
/// <summary>
/// 讀取EXCEL表格所有需要的數(shù)據(jù)到列表
/// </summary>
/// <param name="filePath">路徑</param>
/// <param name="colCount">指定列數(shù),小于1則不指定</param>
/// <param name="needColumn">需要的列號(hào),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
{
//創(chuàng)建Excel文件數(shù)據(jù)流對象
FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
//創(chuàng)建Excel文件對象
IWorkbook workbook = new XSSFWorkbook(fileStream);
//獲取第1個(gè)表單
ISheet worksheet = workbook.GetSheetAt(0);
//獲取單元格值行
int rowCount = worksheet.LastRowNum + 1;
Console.WriteLine($"行數(shù)={rowCount}");
if (rowCount <= 1)
{
return "小于兩行,無數(shù)據(jù)內(nèi)容";
}
//獲取單元格值列,colCount大于等于1,才檢查列數(shù),否則不檢查
int getColumnCount = worksheet.GetRow(0).LastCellNum;
Console.WriteLine($"讀取列數(shù)/確認(rèn)列數(shù)={getColumnCount}/{colCount}");
if (colCount >= 1)
{
if (getColumnCount != colCount)
{
return "列數(shù)不正確";
}
}
int firstRowNum = 0;
for (int i = 0; i < rowCount; i++)//行循環(huán)
{
Console.Write("第" + i + "行=\n");
IRow row = worksheet.GetRow(i);
string rowString = "", needRowString = "";
//是否需要獲取第一行標(biāo)題行
if (needFirstRow)
firstRowNum = 0;
for (int j = 0; j < getColumnCount; j++)//列循環(huán)
{
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表功能函數(shù)設(shè)計(jì)
寫Excel表,即通過已有數(shù)據(jù)導(dǎo)出Excel表,這里講2個(gè)例子。
(1)從DataGridView 導(dǎo)出新的EXCEL
- 顧名思義,創(chuàng)建新的Excel,需要之前沒有文件,是新創(chuàng)建的,所以在fileName定義時(shí),建議添加時(shí)間信息尾綴,當(dāng)然這里也添加了對文件是否存在的檢查,大文件盡量使用xlsx。
- 字段名稱field_names可以默認(rèn)也可以自己定義,因?yàn)橛袝r(shí)候需要使用不同的字段名來展示或者輸出給使用者查看,比如輸出為英文標(biāo)題。
- 這里錯(cuò)誤信息也提供了英中雙語言。
- 格式字體等細(xì)節(jié),大家可以依據(jù)自己的喜好自行調(diào)整選擇,但是建議使用大家都有的字體格式,避免不同系統(tǒng)用戶出現(xiàn)顯示異常。
/// <summary>
/// NPOI DataGridView 導(dǎo)出新的EXCEL (最大行數(shù)要減去1行做標(biāo)題)
/// 03版Excel-xls最大行數(shù)是65535行,最大列數(shù)是256列
/// 07版Excel-xlsx最大行數(shù)是1048575行,最大列數(shù)是16384列
/// </summary>
/// <param name="fileName">保存文件名</param>
/// <param name="dataGridView">DataGridView控件</param>
/// <param name="field_names">字段名稱--為空則使用表格的默認(rèn)字段</param>
/// <param name="err_msg">錯(cuò)誤消息</param>
/// <param name="msg_in_english">錯(cuò)誤消息是否用英文</param>
/// <param name="sheetName">表格名稱--有默認(rèn)值</param>
/// <param name="fontname">字體名稱--有默認(rèn)值</param>
/// <param name="fontsize">字體大小--有默認(rèn)值</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中內(nèi)容是否為空
if (dataGridView.Rows.Count == 0)
{
err_msg = msg_in_english ? "The data content is empty!" : "數(shù)據(jù)內(nèi)容為空!";
return false;
}
//檢測文件是否被占用
if (!IsOccupied(fileName))
{
err_msg = msg_in_english ? $"The file {fileName} is occupied。" : $"文件{fileName}被占用。";
return false;
}
//根據(jù)擴(kuò)展名xls和xlsx來創(chuàng)建對象
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中行數(shù)或列數(shù)超出范圍(最大行數(shù)是 1048575 行,最大列數(shù)是 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中行數(shù)或列數(shù)超出范圍(最大行數(shù)是 65535 行,最大列數(shù)是 256 列)!\r\n要導(dǎo)出數(shù)據(jù),請選擇 Excel文件(*.xlsx)";
return false;
}
else
{
workbook = new HSSFWorkbook();
}
}
//創(chuàng)建Sheet
if (workbook != null)
{
sheet = workbook.CreateSheet(sheetName);//Sheet的名稱
}
else
{
err_msg = msg_in_english ? "Workbook creation failed!" : "workbook創(chuàng)建失敗!";
return false;
}
//程序開始計(jì)時(shí)
sw = new Stopwatch();
sw.Start();
MemoryStream ms = new MemoryStream(); //MemoryStream
//設(shè)置單元格樣式
ICellStyle cellStyle = workbook.CreateCellStyle();
//水平居中對齊和垂直居中對齊
cellStyle.Alignment = NPOI.SS.UserModel.HorizontalAlignment.Center;
cellStyle.VerticalAlignment = NPOI.SS.UserModel.VerticalAlignment.Center;
//設(shè)置字體
IFont font = workbook.CreateFont();
font.FontName = fontname;//字體名稱
font.FontHeightInPoints = fontsize;//字號(hào)
font.Color = NPOI.HSSF.Util.HSSFColor.Black.Index;//字體顏色
cellStyle.SetFont(font);
//添加列名
IRow headRow = sheet.CreateRow(0);
if (field_names == null || field_names.Length == 0)
{
//默認(rèn)使用標(biāo)題
for (int i = 0; i < dataGridView.Columns.Count; i++)
{
//隱藏行列不導(dǎo)出
if (dataGridView.Columns[i].Visible == true)
{
headRow.CreateCell(i).SetCellValue(dataGridView.Columns[i].HeaderText);
headRow.GetCell(i).CellStyle = cellStyle;
}
}
}
else
{
//自定義標(biāo)題
for (int i = 0; i < field_names.Length; i++)
{
headRow.CreateCell(i).SetCellValue(field_names[i]);
headRow.GetCell(i).CellStyle = cellStyle;
}
}
//根據(jù)類型寫入內(nèi)容
for (int rowNum = 0; rowNum < dataGridView.Rows.Count; rowNum++)
{
///跳過第一行,第一行為列標(biāo)題
IRow dataRow = sheet.CreateRow(rowNum + 1);
for (int columnNum = 0; columnNum < dataGridView.Columns.Count; columnNum++)
{
int columnWidth = (int)sheet.GetColumnWidth(columnNum) / 256; //列寬
//隱藏行列不導(dǎo)出
if (dataGridView.Rows[rowNum].Visible == true && dataGridView.Columns[columnNum].Visible == true)
{
//防止行列超出Excel限制
if (fileExt == ".xls")
{
//03版Excel最大行數(shù)是65535行,最大列數(shù)是256列
if (rowNum > 65535)
{
err_msg = msg_in_english ? "The number of rows in the xls file exceeds the limit set by Excel!" : "xls行數(shù)超過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列數(shù)超過Excel限制!";
return false;
}
}
else if (fileExt == ".xlsx")
{
//07版Excel最大行數(shù)是1048575行,最大列數(shù)是16384列
if (rowNum > 1048575)
{
err_msg = msg_in_english ? "The number of rows in the xlsx file exceeds the limit set by Excel" : "xlsx行數(shù)超過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列數(shù)超過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());//所有數(shù)據(jù)直接按文本輸出,簡化操作。
}
//設(shè)置列寬
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; //設(shè)置列寬數(shù)值
}
}
sheet.SetColumnWidth(columnNum, columnWidth * 256);
//單元格樣式
dataRow.GetCell(columnNum).CellStyle = cellStyle;
//特定單元格紅色背景設(shè)置為紅色背景
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;
//設(shè)置字體
font.FontName = fontname;//字體名稱
font.FontHeightInPoints = fontsize;//字號(hào)
font.Color = NPOI.HSSF.Util.HSSFColor.Black.Index;//字體顏色
icellStyle.SetFont(font);
//設(shè)置單元格顏色
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;//設(shè)置
}
}
}
}
//保存為Excel文件
workbook.Write(ms);
FileStream file = new FileStream(fileName, FileMode.Create);
workbook.Write(file);
file.Close();
workbook = null;
ms.Close();
ms.Dispose();
//程序結(jié)束計(jì)時(shí)//
sw.Stop();
double totalTime = sw.ElapsedMilliseconds / 1000.0;
err_msg = msg_in_english ? $"{fileName} export successful,spend {totalTime}s。" : $"{fileName}導(dǎo)出成功,耗時(shí){totalTime}秒。";
return true;
}
(2)將DataGridView導(dǎo)出添加到EXCEL
- 這個(gè)例子和上面本質(zhì)上是有著異曲同工之妙, 不同的是如果文件存在則是在原文件尾繼續(xù)添加數(shù)據(jù),特別適合匯總的Excel表文件。
- 當(dāng)然新文件可以添加,但新文件需要首行標(biāo)題的話需要提前添加,可以結(jié)合前一個(gè)接口程序。
/// <summary>
/// 將DataGridView導(dǎo)出添加到EXCEL(NPOI)
/// </summary>
/// <param name="fileName">保存的文件名</param>
/// <param name="dataGridView">DataGridView數(shù)據(jù)控件</param>
/// <param name="err_msg">錯(cuò)誤消息</param>
/// <param name="msg_in_english">錯(cuò)誤消息是否用英文</param>
/// <param name="sheetName">表格名稱--有默認(rèn)值</param>
/// <param name="fontname">字體名稱--有默認(rèn)值</param>
/// <param name="fontsize">字體大小--有默認(rèn)值</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;
}
//獲取擴(kuò)展名xls和xlsx
string fileExt = Path.GetExtension(fileName).ToLower();
// 打開現(xiàn)有的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)獲取第一個(gè)工作表
else
sheet = workbook.GetSheet(sheetName); //指定工作表
// 檢查sheet是否為空,如果不存在則創(chuàng)建新的sheet
if (sheet == null)
{
sheet = workbook.CreateSheet(sheetName);
}
MemoryStream ms = new MemoryStream(); //MemoryStream
// 創(chuàng)建單元格樣式
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;
//設(shè)置字體
IFont font = workbook.CreateFont();
font.FontName = fontname;//字體名稱
font.FontHeightInPoints = fontsize;//字號(hào)
font.Color = NPOI.HSSF.Util.HSSFColor.Black.Index;//字體顏色
cellStyle.SetFont(font);
int last_row_num = sheet.LastRowNum; // 獲取尾行的行號(hào)
//根據(jù)類型寫入內(nèi)容
for (int rowNum = 0; rowNum < dataGridView.Rows.Count; rowNum++)
{
IRow row = sheet.CreateRow(++last_row_num); // 創(chuàng)建新行,行號(hào)+1
for (int columnNum = 0; columnNum < dataGridView.Columns.Count; columnNum++)
{
//隱藏行列不導(dǎo)出
if (dataGridView.Rows[rowNum].Visible == true && dataGridView.Columns[columnNum].Visible == true)
{
//防止行列超出Excel限制
if (fileExt == ".xls")
{
//03版Excel最大行數(shù)是65535行,最大列數(shù)是256列
if (rowNum > 65535)
{
err_msg = msg_in_english ? "The number of rows in the xls file exceeds the limit set by Excel!" : "xls行數(shù)超過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列數(shù)超過Excel限制!";
return false;
}
}
else if (fileExt == ".xlsx")
{
//07版Excel最大行數(shù)是1048575行,最大列數(shù)是16384列
if (rowNum > 1048575)
{
err_msg = msg_in_english ? "The number of rows in the xlsx file exceeds the limit set by Excel" : "xlsx行數(shù)超過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列數(shù)超過Excel限制!";
return false;
}
}
//寫入數(shù)據(jù)
//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());//所有數(shù)據(jù)直接按文本輸出
}
//單元格樣式
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)引用系統(tǒng)API檢測文件是否被占用
- 在保存文件的時(shí)候經(jīng)常需要用到
#region 檢測文件是否被占用
// 判斷文件是否打開
[DllImport("kernel32.dll")]
public static extern IntPtr _lopen(string lpPathName, int iReadWrite);
// 關(guān)閉文件句柄
[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 //檢測文件是否被占用
總結(jié)
本文通過NPIO接口C#語言描述了3個(gè)Excel導(dǎo)入的函數(shù)和2個(gè)導(dǎo)出的行數(shù),各有千秋,大同小異。朋友們可以自己研究,看看有沒有比NPOI更加好的方案來控制Excel的讀寫,可以告訴我。
以上就是C#通過NPIO讀寫Excel表的方法詳解的詳細(xì)內(nèi)容,更多關(guān)于C# NPIO讀寫Excel表的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C#實(shí)現(xiàn)xml文件的讀取與寫入簡單實(shí)例
這篇文章主要介紹了C#實(shí)現(xiàn)xml文件的讀取與寫入方法,涉及C#操作XML文件的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-08-08
C#高效解析并提取PDF文檔中的文本與表格實(shí)戰(zhàn)指南
PDF文檔作為一種通用、安全且跨平臺(tái)的文檔格式,已成為現(xiàn)代業(yè)務(wù)流程中不可或缺的一部分,本文將深入探討如何利用C#結(jié)合高效的PDF處理庫,實(shí)現(xiàn)PDF文檔的自動(dòng)化解析,從基礎(chǔ)的環(huán)境配置到復(fù)雜的表格數(shù)據(jù)提取,為您提供一套全面的實(shí)踐指南2025-12-12
使用C#和WinForms創(chuàng)建動(dòng)態(tài)圖表的兩種方法
本文詳細(xì)介紹了如何使用C#和WinForms中的Chart控件創(chuàng)建動(dòng)態(tài)圖表,包括實(shí)例化圖表、添加數(shù)據(jù)點(diǎn)、使用多線程實(shí)現(xiàn)實(shí)時(shí)更新,并提供兩種方法創(chuàng)建圖表實(shí)例,需要的朋友可以參考下2025-09-09
c# Newtonsoft 六個(gè)值得使用的特性(下)
這篇文章主要介紹了c# Newtonsoft 六個(gè)值得使用的特性,文中示例代碼非常詳細(xì),幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下2020-06-06
C#實(shí)現(xiàn)順序表(線性表)完整實(shí)例
這篇文章主要介紹了C#實(shí)現(xiàn)順序表(線性表)的方法,結(jié)合完整實(shí)例形式分析了順序表的原理及C#相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2016-06-06
C#使用NPOI操作Excel工具類的實(shí)現(xiàn)
NPOI是POI項(xiàng)目的.NET遷移版本,使用NPOI可以在沒有安裝Office或者相應(yīng)環(huán)境的機(jī)器上對Word或Excel文檔進(jìn)行讀寫操作,下面我們就來學(xué)習(xí)一下如何使用NPOI編寫操作Excel的工具類吧2023-11-11

