本文介绍在C#代码中如何使用NPOI读取和写入Excel数据。
首先,通过Visual Studio菜单“工具”>>“NuGet包管理”>>“管理解决方案的NuGet程序包”,打开NuGet管理窗口;然后,在“浏览”页中搜索“npoi”,选中“NPOI”,并安装到当前项目中。如下图所示。
点击“安装”后接受一些协议,然后等待安装完成即可。
下面的代码(cfx/office/tExcelWriter.cs)是tExcelWriter类的一部分,可复制到Visual Studio中阅读和测试。
using System; using System.IO; using System.Data; using NPOI.SS.UserModel; using NPOI.HSSF.UserModel; using NPOI.XSSF.UserModel; namespace cfx.office { public class tExcelWriter { private string myFileName; private DataTable myData; private string mySheetName; private int myBeginRow; private int myBeginColumn; private bool myWriteColumnName; // private tExcelWriter() { } // public static tExcelWriter Create(string filename, DataTable data, string sheetName = null, int beginRow = 1, int beginCol = 1, bool writeColumnName = true) { return new tExcelWriter() { myFileName = filename, myData = data, mySheetName = sheetName, myBeginRow = beginRow - 1, myBeginColumn = beginCol - 1, myWriteColumnName = writeColumnName }; } // public bool Write() { string ext = Path.GetExtension(myFileName).ToLower(); if (ext == ".xls") return WriteXls(); else return WriteXlsx(); } // 将DataTable对象数据写入.xls文件指定工作表的指定位置 private bool WriteXls() { try { if (myData == null || myData.Columns.Count < 1) return false; // using (HSSFWorkbook wb = new HSSFWorkbook()) { if (mySheetName == null || mySheetName.Trim().Length == 0) mySheetName = "数据"; ISheet sheet = wb.CreateSheet(mySheetName); // 写入工作表 int curRow = myBeginRow; // 写入列名 if (myWriteColumnName) { IRow sheetRow = sheet.CreateRow(curRow); for (int col = 0; col < myData.Columns.Count; col++) { ICell cell = sheetRow.CreateCell(col + myBeginColumn); cell.SetCellType(CellType.String); cell.SetCellValue(myData.Columns[col].ColumnName); } curRow++; } // 日期和时间格式 IDataFormat dataFormat = wb.CreateDataFormat(); ICellStyle datetimeStyle = wb.CreateCellStyle(); datetimeStyle.DataFormat = dataFormat.GetFormat("yyyy/MM/dd HH:mm:ss"); // 写入数据行 for (int row = 0; row < myData.Rows.Count; row++) { IRow sheetRow = sheet.CreateRow(curRow++); for (int col = 0; col < myData.Columns.Count; col++) { ICell cell = sheetRow.CreateCell(col + myBeginColumn); if (myData.Rows[row][col] == DBNull.Value) { cell.SetCellValue(""); } else { Type dataType = myData.Columns[col].DataType; if (Comm.IsNumeric(dataType)) { cell.SetCellType(CellType.Numeric); double result; if (Obj.TryToDbl(myData.Rows[row][col], out result)) cell.SetCellValue(result); } else if (dataType.Name == "DateTime") { cell.CellStyle = datetimeStyle; DateTime result; if (Obj.TryToDate(myData.Rows[row][col], out result)) cell.SetCellValue(result); } else if (dataType.Name == "Boolean") { cell.SetCellType(CellType.Boolean); cell.SetCellValue(Obj.ToBool(myData.Rows[row][col])); } else { cell.SetCellType(CellType.String); cell.SetCellValue(Obj.ToStr(myData.Rows[row][col])); } } } } // using (FileStream fs = new FileStream(myFileName, FileMode.Create, FileAccess.Write)) { wb.Write(fs); } return true; } } catch { return false; } } // 将DataTable对象数据写入.xlsx文件指定工作表的指定位置 private bool WriteXlsx() { try { if (myData == null || myData.Columns.Count < 1) return false; // using (XSSFWorkbook wb = new XSSFWorkbook()) { if (mySheetName == null || mySheetName.Trim().Length == 0) mySheetName = "数据"; ISheet sheet = wb.CreateSheet(mySheetName); // 写入工作表 int curRow = myBeginRow; // 写入列名 if (myWriteColumnName) { IRow sheetRow = sheet.CreateRow(curRow); for (int col = 0; col < myData.Columns.Count; col++) { ICell cell = sheetRow.CreateCell(col + myBeginColumn); cell.SetCellType(CellType.String); cell.SetCellValue(myData.Columns[col].ColumnName); } curRow++; } // 日期和时间格式 IDataFormat dataFormat = wb.CreateDataFormat(); ICellStyle datetimeStyle = wb.CreateCellStyle(); datetimeStyle.DataFormat = dataFormat.GetFormat("yyyy/MM/dd HH:mm:ss"); // 写入数据行 for (int row = 0; row < myData.Rows.Count; row++) { IRow sheetRow = sheet.CreateRow(curRow++); for (int col = 0; col < myData.Columns.Count; col++) { ICell cell = sheetRow.CreateCell(col + myBeginColumn); if (myData.Rows[row][col] == DBNull.Value) { cell.SetCellValue(""); } else { Type dataType = myData.Columns[col].DataType; if (Comm.IsNumeric(dataType)) { cell.SetCellType(CellType.Numeric); double result; if (Obj.TryToDbl(myData.Rows[row][col], out result)) cell.SetCellValue(result); } else if (dataType.Name == "DateTime") { cell.CellStyle = datetimeStyle; DateTime result; if (Obj.TryToDate(myData.Rows[row][col], out result)) cell.SetCellValue(result); } else if (dataType.Name == "Boolean") { cell.SetCellType(CellType.Boolean); cell.SetCellValue(Obj.ToBool(myData.Rows[row][col])); } else { cell.SetCellType(CellType.String); cell.SetCellValue(Obj.ToStr(myData.Rows[row][col])); } } } } // using (FileStream fs = new FileStream(myFileName, FileMode.Create, FileAccess.Write)) { wb.Write(fs); } return true; } } catch { return false; } } // 其它代码 } }
tExcelWriter类中,首先定义了6个内部字段,分别保存写入Excel时需要的参数,包括:
接下来是私有的构造函数,意味着不能在tExcelWriter类的外部使用new关键字创建实例,而创建实例的工作由Create()静态方法完成,方法中包含6个参数,对应了6个内部字段,分别是:
接下来定义了三个方法,分别是:
WriteXls()方法和WriteXlsx()方法的代码几乎一样,只是在创建工作簿(Workbook)的时候使用了不同的类型,在处理.xls文件时使用了HSSFWorkbook类型,处理.xlsx文件时使用了XSSFWorkbook类型。这里,并没有将重复代码定义为单独的方法,在处理不同类型Excel文件时,如果对代码有特殊要求,调整起来会更加方便。
下面以WriteXls()方法为例进行说明。创建工作簿后,首先指定写入工作表的名称,如果指定的mySheetName为null或空白字符串,则使用“数据”;然后,使用工作簿对象的CreateSheet()方法创建工作表(Worksheet)对象。
创建行对象(IRow类型)时,使用工作表对象的CreateRow()方法,其参数为从0开始的行索引(行号),即第一行索引为0、第二行索引1,以此类推。
创建单元格对象(ICell类型)时,使用行对象的CreateCell()方法,其参数为从0开始的列索引(列号)。
单元格对象的SetCellType()方法用于设置单元格类型,其参数使用CellType枚举,成员包括Numeric(0)、String(1)、Formula(2)、Blank(3)、Boolean(4)、Error(5)。
单元格对象的SetCellValue()方法可以设置单元格的值,方法有不同的重载版本,参数分别是string、double、bool、DateTime和IRichTextString类型。
设置单元格格式时使用了IDataFormat和ICellStyle对象,方法中设置显示日期和时间数据格式为“年/月/日 时:分:秒”。
接下来会逐行写入数据,需要注意,单元格数据会根据DataTable对象中列的数据类型分别进行操作。
DBNull值时写入空白字符串。
数值类型时会转换为double类型写入。这里使用Obj.TryToDbl()方法转换,相关定义如下(cfx/Obj.cs):
using System; namespace cfx { public static class Obj { // 其它代码 // public static string ToStr(object obj, string defVal = "") { if (obj != null) return obj.ToString(); else return defVal; } // public static bool ToBool(object obj) { try { return Convert.ToBoolean(obj); } catch { return false; } } // 尝试转换类型 public static bool TryToDbl(object obj,out double result) { result = 0D; return obj != null && double.TryParse(obj.ToString(), out result); } public static bool TryToDate(object obj, out DateTime result) { result = DateTime.MinValue; return obj != null && DateTime.TryParse(obj.ToString(), out result); } } }
DateTime类型数据会通过Obj.TryToDate()方法转换后写入,转换失败不写入单元格。
Boolean类型时通过Obj.ToBool()方法转换后写入。
其它类型则写入string类型数据。
保存工作簿时使用了Write()方法,其参数使用了FileStream对象,注意FileStream构造函数的使用,参数一指定了文件的路径,这里是需要保存Excel的文件路径;参数二指定打开模式,这里设置为创建(FileMode.Create),此操作会覆盖已存在的同名文件;参数二指定访问模式,这里指定FileAccess.Write,表示写入。
操作成功时,WriteXls()方法返回true,否则返回false。
下面的代码,在Program.cs文件中测试tExcelWriter类的使用。
using System; using System.Data; using cfx.office; namespace csfx_demo { class Program { static void Main(string[] args) { DataTable data = tApp.DbJet.GetTable(@"select * from t1"); bool result = tExcelWriter.Create(@"d:\t1.xls", data).Write(); Console.WriteLine(result); } } }
代码会读取t1表中的所有数据,然后写入d:\t1.xls文件,操作成功会显示True。写入内容如下图所示。
下面的代码(cfx/office/tExcelWriter.cs)定义了WriteExisted()和相关方法,用于将数据写入已存在的Excel文件。
using System; using System.IO; using System.Data; using NPOI.SS.UserModel; using NPOI.HSSF.UserModel; using NPOI.XSSF.UserModel; namespace cfx.office { public class tExcelWriter { // 其它代码 // 写入已存在的文件 public bool WriteExisted() { string ext = Path.GetExtension(myFileName).ToLower(); if (ext == ".xls") return WriteXlsExisted(); else return WriteXlsxExisted(); } // 写入已存在的xls文件 private bool WriteXlsExisted() { try { if (myData == null || myData.Columns.Count < 1 || File.Exists(myFileName) == false) return false; // using (FileStream fs = new FileStream(myFileName, FileMode.Open, FileAccess.Read)) { using (HSSFWorkbook wb = new HSSFWorkbook(fs)) { int sheetIndex = 0; if (mySheetName != null && mySheetName.Trim().Length > 0) sheetIndex = wb.GetSheetIndex(mySheetName); if (sheetIndex == -1) sheetIndex = 0; ISheet sheet = wb.GetSheetAt(sheetIndex); // int curRow = myBeginRow; // 写入列名 if (myWriteColumnName) { IRow sheetRow = sheet.GetRow(curRow); if (sheetRow == null) sheetRow = sheet.CreateRow(curRow); for (int col = 0; col < myData.Columns.Count; col++) { int colIndex = col + myBeginColumn; ICell cell = sheetRow.GetCell(colIndex); if (cell == null) cell = sheetRow.CreateCell(colIndex); cell.SetCellType(CellType.String); cell.SetCellValue(myData.Columns[col].ColumnName); } curRow++; } // 日期和时间格式 IDataFormat dataFormat = wb.CreateDataFormat(); ICellStyle datetimeStyle = wb.CreateCellStyle(); datetimeStyle.DataFormat = dataFormat.GetFormat("yyyy/MM/dd HH:mm:ss"); // 写入数据行 for (int row = 0; row < myData.Rows.Count; row++) { IRow sheetRow = sheet.GetRow(curRow); if (sheetRow == null) sheetRow = sheet.CreateRow(curRow); for (int col = 0; col < myData.Columns.Count; col++) { int colIndex = col + myBeginColumn; ICell cell = sheetRow.GetCell(colIndex); if (cell == null) cell = sheetRow.CreateCell(colIndex); if (myData.Rows[row][col] == DBNull.Value) { cell.SetCellValue(""); } else { Type dataType = myData.Columns[col].DataType; if (Comm.IsNumeric(dataType)) { cell.SetCellType(CellType.Numeric); double result; if (Obj.TryToDbl(myData.Rows[row][col], out result)) cell.SetCellValue(result); } else if (dataType.Name == "DateTime") { cell.CellStyle = datetimeStyle; DateTime result; if (Obj.TryToDate(myData.Rows[row][col], out result)) cell.SetCellValue(result); } else if (dataType.Name == "Boolean") { cell.SetCellType(CellType.Boolean); cell.SetCellValue(Obj.ToBool(myData.Rows[row][col])); } else { cell.SetCellType(CellType.String); cell.SetCellValue(Obj.ToStr(myData.Rows[row][col])); } } } curRow++; } // using (FileStream fs1 = new FileStream(myFileName, FileMode.Create, FileAccess.Write)) { wb.Write(fs1); } } return true; } } catch { return false; } } // 写入已存在的xls文件 private bool WriteXlsxExisted() { try { if (myData == null || myData.Columns.Count < 1 || File.Exists(myFileName) == false) return false; // using (FileStream fs = new FileStream(myFileName, FileMode.Open, FileAccess.Read)) { using (XSSFWorkbook wb = new XSSFWorkbook(fs)) { int sheetIndex = 0; if (mySheetName != null && mySheetName.Trim().Length > 0) sheetIndex = wb.GetSheetIndex(mySheetName); if (sheetIndex == -1) sheetIndex = 0; ISheet sheet = wb.GetSheetAt(sheetIndex); // int curRow = myBeginRow; // 写入列名 if (myWriteColumnName) { IRow sheetRow = sheet.GetRow(curRow); if (sheetRow == null) sheetRow = sheet.CreateRow(curRow); for (int col = 0; col < myData.Columns.Count; col++) { int colIndex = col + myBeginColumn; ICell cell = sheetRow.GetCell(colIndex); if (cell == null) cell = sheetRow.CreateCell(colIndex); cell.SetCellType(CellType.String); cell.SetCellValue(myData.Columns[col].ColumnName); } curRow++; } // 日期和时间格式 IDataFormat dataFormat = wb.CreateDataFormat(); ICellStyle datetimeStyle = wb.CreateCellStyle(); datetimeStyle.DataFormat = dataFormat.GetFormat("yyyy/MM/dd HH:mm:ss"); // 写入数据行 for (int row = 0; row < myData.Rows.Count; row++) { IRow sheetRow = sheet.GetRow(curRow); if (sheetRow == null) sheetRow = sheet.CreateRow(curRow); for (int col = 0; col < myData.Columns.Count; col++) { int colIndex = col + myBeginColumn; ICell cell = sheetRow.GetCell(colIndex); if (cell == null) cell = sheetRow.CreateCell(colIndex); if (myData.Rows[row][col] == DBNull.Value) { cell.SetCellValue(""); } else { Type dataType = myData.Columns[col].DataType; if (Comm.IsNumeric(dataType)) { cell.SetCellType(CellType.Numeric); double result; if (Obj.TryToDbl(myData.Rows[row][col], out result)) cell.SetCellValue(result); } else if (dataType.Name == "DateTime") { cell.CellStyle = datetimeStyle; DateTime result; if (Obj.TryToDate(myData.Rows[row][col], out result)) cell.SetCellValue(result); } else if (dataType.Name == "Boolean") { cell.SetCellType(CellType.Boolean); cell.SetCellValue(Obj.ToBool(myData.Rows[row][col])); } else { cell.SetCellType(CellType.String); cell.SetCellValue(Obj.ToStr(myData.Rows[row][col])); } } } curRow++; } // using (FileStream fs1 = new FileStream(myFileName, FileMode.Create, FileAccess.Write)) { wb.Write(fs1); } } return true; } } catch { return false; } } } }
需要将数据写入已存在的Excel文件时,创建工作簿对象的方式有所不同,在创建HSSFWorkbook或XSSFWorkbook对象时,需要使用一个包含写入文件路径的FileStream对象作为构造方法的参数,设置模式为打开(FileMode.Open),访问方式为读取(FileAccess.Read)即可。
获取工作表对象时,如果指定的工作表名称(mySheetName)为null、空白字符串,或者名称不存在,则默认使用第一个工作表(索引0)。其中,工作簿对象的GetSheetIndex()方法会根据工作表名返回索引值,工作表不存在时返回-1。
获取行对象(IRow类型)时,为避免覆盖工作表中已存在的内容,首先会使用工作表对象的GetRow()方法获取,如果行不存在返回null,此时再使用CreateRow()方法创建行。单元格对象(ICell类型)的处理也是这样,首先会使用行对象的GetCell()方法获取单元格对象,如果单元格对象不存在会返回null,此时使用CreateCell()方法创建新的单元格对象。
最后,在保存Excel文件时要注意,这里会创建一个新的FileStream对象,设置打开模式为创建(FileMode.Create),访问方式为写入(FileAccess.Write);然后调用工作簿对象的Write()方法覆盖写入目标文件即可。
接下来,可以在d:\t1.xls文件中创建一个新的工作表,命名为“数据1”,然后在Program.cs文件中修改代码如下。
using System; using System.Data; using cfx.office; namespace csfx_demo { class Program { static void Main(string[] args) { DataTable data = tApp.DbJet.GetTable(@"select * from t1"); bool result = tExcelWriter.Create(@"d:\t1.xls", data,"数据1").WriteExisted(); Console.WriteLine(result); } } }
导出数据如下图所示。
实际工作中,可能还会先定义Excel模板,每次导出数据会复制模板文件到一个新的文件,然后写入数据。下面的代码(cfx/office/tExcelWriter.cs),在tExcelWriter类中添加WriteByTemplate()方法,用于将数据导出到Excel模板文件。
using System; using System.IO; using System.Data; using NPOI.SS.UserModel; using NPOI.HSSF.UserModel; using NPOI.XSSF.UserModel; namespace cfx.office { public class tExcelWriter { // 其它代码 // 按模板写入,参数为模板文件路径 public bool WriteByTemplate(string template) { try { if (File.Exists(template) == false) return false; File.Copy(template, myFileName, true); return WriteExisted(); } catch { return false; } } // } }
WriteByTemplate()方法的参数需要指定模板文件的路径,方法中会根据文件扩展名分别调用WriteXlsExisted()和WriteXlsxExisted()方法。
接下来,在d:盘下创建template.xlsx文件,并修改第一个工作表内容如下图所示。
下面的代码,在Program.cs文件中测试按模板导出数据。
using System; using System.Data; using cfx.office; namespace csfx_demo { class Program { static void Main(string[] args) { DataTable data = tApp.DbJet.GetTable(@"select * from t1"); bool result = tExcelWriter.Create(@"d:\t1.xlsx", data, null, 5, 3, false) .WriteByTemplate(@"d:\template.xlsx"); Console.WriteLine(result); } } }
执行代码,会复制d:\template.xlsx文件到d:\t1.xlsx,然后从第5行第3列开始写入数据,完成效果如下图所示。
接下来会实现读取Excel数据的功能,并将读取的数据保存到DataTable对象。下面的代码(cfx/office/tExcelReader.cs)创建tExcelReader类实现Excel数据读取功能。
using System; using System.Data; using NPOI.SS.UserModel; namespace cfx.office { public class tExcelReader { // Excel文件名 private string myFileName; // 读取式作表索引 private int mySheetIndex; // 如果设置工作表名称,则优先使用 private string mySheetName; // 开始读取数据的行和列 private int myBeginColumn; private int myBeginRow; // 第一行作为列名 private bool myFirstRowAsColumnName; // 读取的行数,大于0时有效,否则读取全部 private int myReadRowCount; // private tExcelReader() { } // public static tExcelReader Create(string filename, int sheetIndex = 0, string sheetName = null, int beginRow = 1, int beginCol = 1, bool firstRowAsColName = true, int readRowCount = -1) { return new tExcelReader() { myFileName = filename, mySheetIndex = sheetIndex, mySheetName = sheetName, myBeginRow = beginRow - 1, myBeginColumn = beginCol - 1, myFirstRowAsColumnName = firstRowAsColName, myReadRowCount = readRowCount }; } // 执行读取操作 public DataTable Read() { try { IWorkbook wb = WorkbookFactory.Create(myFileName); // 优先使用名称获取工作表 if (mySheetName != null) mySheetIndex = wb.GetSheetIndex(mySheetName); if (mySheetIndex < 0 || mySheetIndex > wb.NumberOfSheets - 1) mySheetIndex = 0; ISheet sheet = wb.GetSheetAt(mySheetIndex); // 读取第一行列数为全部行读取列数 int colCount = sheet.GetRow(myBeginRow).LastCellNum; int rowCount = sheet.LastRowNum + 1; // 读取数据并添加到DataTable对象 DataTable tbl = new DataTable(); // 添加DataTable对象的列 for (int col = myBeginColumn; col < colCount; col++) tbl.Columns.Add(); // IRow readRow; // 读取行数据计数 int readRowCounter = 0; // for (int row = myBeginRow; row < rowCount; row++) { readRow = sheet.GetRow(row); DataRow dataRow = tbl.NewRow(); for (int col = myBeginColumn; col < colCount; col++) { int tblColIndex = col - myBeginColumn; try { ICell cell = readRow.GetCell(col); if (cell == null) { dataRow[tblColIndex] = DBNull.Value; } else if (cell.CellType == CellType.Numeric) { // Excel中的日期时间值是Double类型 if (DateUtil.IsCellDateFormatted(cell)) dataRow[tblColIndex] = cell.DateCellValue; else dataRow[tblColIndex] = cell.NumericCellValue; } else if (cell.CellType == CellType.Boolean) { dataRow[tblColIndex] = cell.BooleanCellValue ? 1 : 0; } else { // 其它格式以文本形式显示,纯空白字符设置为空值 string val = cell.StringCellValue.Trim(); if (val.Length == 0) dataRow[tblColIndex] = DBNull.Value; else dataRow[tblColIndex] = val; } } catch { dataRow[tblColIndex] = DBNull.Value; } } tbl.Rows.Add(dataRow); // 读取行 readRowCounter++; if (myReadRowCount > 0 && myReadRowCount == readRowCounter) break; } // 数据第一行提升为标题,然后从数据行删除 if (myFirstRowAsColumnName) { string sName; for (int col = 0; col < tbl.Columns.Count; col++) { sName = tbl.Rows[0][col].ToString().Trim(); if (sName != "") tbl.Columns[col].ColumnName = sName; } tbl.Rows.RemoveAt(0); } // return tbl; } catch { return null; } } // } }
tExcelReader类中,首先定义了一些内部字段,用于保存读取Excel数据的参数;私有构造函数确定不能在tExcelReader类的外部使用new关键字创建实例;Create()静态方法用于创建tExcelReader实例,其参数与这些字段一一对应,方法的参数包括:
数据读取操作使用Read()方法完成。方法中,首先使用WorkbookFactory.Create()方法创建工作簿对象,参数为Excel文件路径;此方法会自动识别文件类型。
接下来,确定读取的工作表时会优先使用工作表名称,如果没有指定工作表名称则使用索引值,如果索引值小于0或大于等于工作表的数量则默认使用0,即读取第一个工作表。
方法中,使用读取的第一行的列数为全部行的读取列数,这里使用了行对象的LastCellNum属性,此属性为只读属性,返回行中的单元格数量(最大索引加1)。
获取工作表的行数时,使用工作表对象的LastRowNum属性加1,其中,LastRowNum属性会返回最大的行索引值。
读取单元格数据时会分别处理以下情况:
下面了解一些DataTable对象的动态操作。
首先,添加列时可以使用DataTable对象Columns集合中的Add()方法,方法可以使用指定的列名作为参数,不指定列名时,列名会自动使用Column1、Column2、……。获取列对象(DataColumn)时,可以在Columns集合中使用从0开始的数值索引或列名索引,读取列名时可以使用DataColumn对象的ColumnName属性。
创建新行时,使用DataTable对象的NewRow()方法,方法会返回一个新的DataRow对象。此时,行对象中的数据单元会自动匹配表中列的设置。
行对象中,可以使用0 开始的数值索引或列名索引获取列对应的数据单元。
将行对象添加到DataTable对象时,使用Rows集合中的Add()方法。
在Read()方法的最后,如果需要将第一行提升为列名,会将第一行数据设置为对应的列名,然后将第一行数据删除。删除行时使用了Rows集合中的RemoveAt()方法,参数为删除行的索引(从0开始)。这里应注意,如对应的数据为空字符串或纯空白字符,则不作为列名。
假设d:\t1.xls文件中有如下图所示的数据。
下面的代码,在Program.cs文件中测试tExcelReader类的使用。
using System; using System.Data; using cfx.office; namespace csfx_demo { class Program { static void Main(string[] args) { DataTable data = tExcelReader.Create(@"d:\t1.xls").Read(); Console.WriteLine(data.Rows.Count); Console.WriteLine(data.Columns.Count); for(int col = 0; col < data.Columns.Count; col++) { Console.WriteLine(data.Columns[col].ColumnName); } } } }
代码会读取d:\t1.xls文件中第一个工作表的数据,读取位置默认为第一行第一列。执行代码会显示读取完成后的数据行数、列数和列名,结果如下图所示。