C#读取Excel的三种方式以及比较分析

(1)OleDB方式

优点:将Excel直接当做数据源处理,通过SQL直接读取内容,读取速度较快。

缺点:读取数据方式不够灵活,无法直接读取某一个单元格,只有将整个Sheet页读取出来后(结果为Datatable)再在Datatable中根据行列数来获取指定的值。

当Excel数据量很大时。会非常占用内存,当内存不够时会抛出内存溢出的异常。

读取代码如下:

 public DataTable GetExcelTableByOleDB(string strExcelPath, string tableName)
 {
   try
   {
     DataTable dtExcel = new DataTable();
     //数据表
     DataSet ds = new DataSet();
     //获取文件扩展名
     string strExtension = System.IO.Path.GetExtension(strExcelPath);
     string strFileName = System.IO.Path.GetFileName(strExcelPath);
     //Excel的连接
     OleDbConnection objConn = null;
     switch (strExtension)
     {
       case ".xls":
         objConn = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + strExcelPath + ";" + "Extended Properties=\"Excel 8.0;HDR=NO;IMEX=1;\"");
         break;
       case ".xlsx":
         objConn = new OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + strExcelPath + ";" + "Extended Properties=\"Excel 12.0;HDR=NO;IMEX=1;\"");
         break;
       default:
         objConn = null;
         break;
     }
     if (objConn == null)
     {
       return null;
     }
     objConn.Open();
     //获取Excel中所有Sheet表的信息
     //System.Data.DataTable schemaTable = objConn.GetOleDbSchemaTable(System.Data.OleDb.OleDbSchemaGuid.Tables, null);
     //获取Excel的第一个Sheet表名
     //string tableName = schemaTable.Rows[0][2].ToString().Trim();
     string strSql = "select * from [" + tableName + "]";
     //获取Excel指定Sheet表中的信息
     OleDbCommand objCmd = new OleDbCommand(strSql, objConn);
     OleDbDataAdapter myData = new OleDbDataAdapter(strSql, objConn);
     myData.Fill(ds, tableName);//填充数据
     objConn.Close();
     //dtExcel即为excel文件中指定表中存储的信息
     dtExcel = ds.Tables[tableName];
     return dtExcel;
   }
   catch
   {
     return null;
   }
 }

下面说明一下连接字符串

HDR=Yes,这代表第一行是标题,不做为数据使用(但是我在实际使用中,如果第一行存在复杂数值,那么读取得到的Datatable列标题会自动设置为F1、F2等方式命名,与实际应用不符,所以当时是通过HDR=No方式将所有内容读取到Datatable中,然后手动将第一行设置成标题的);IMEX ( IMport EXport mode )设置
IMEX 有三种模式:
0 is Export mode
1 is Import mode
2 is Linked mode (full update capabilities)
我这里特别要说明的就是 IMEX 参数了,因为不同的模式代表著不同的读写行为:
当 IMEX=0 时为“汇出模式”,这个模式开启的 Excel 档案只能用来做“写入”用途。
当 IMEX=1 时为“汇入模式”,这个模式开启的 Excel 档案只能用来做“读取”用途。
当 IMEX=2 时为“链接模式”,这个模式开启的 Excel 档案可同时支援“读取”与“写入”用途。

---------------------------------

另外,读取Excel2007版本的文件时,版本应该从8.0改为12.0,同时驱动不能再用Jet,而应该用ACE。负责会造成“找不到可安装的 ISAM”的错误。

---------------------------------

在网上还发现采用这种方式存在取出的Sheet表的个数多于实际Excel表中的Sheet表个数的情况,其原因有二:

1. 取出的名称中,包括了XL命名管理器中的名称(参见XL2007的公式--命名管理器, 快捷键Crtl+F3);

2. 取出的名称中,包括了FilterDatabase后缀的, 这是XL用来记录Filter范围的。

对于第一点比较简单, 删除已有命名管理器中的内容即可;第二点处理起来比较麻烦, Filter删除后这些名称依然保留着,简单的做法是新增Sheet然后将原Sheet Copy进去。但实际情况并不能为每个Excel做以上检查。下面给出了过滤的方案。(此问题我们有验证过,大家自己验证一下吧)

 //objConn为读取Excel的链接,下面通过过滤来获取有效的Sheet页名称集合
  System.Data.DataTable schemaTable = objConn.GetOleDbSchemaTable(System.Data.OleDb.OleDbSchemaGuid.Tables, null);
  List<string> lstSheetNames = new List<string>();
  for (int i = 0; i < schemaTable.Rows.Count; i++)
  {
    string strSheetName = (string)dtSheetName.Rows[i]["TABLE_NAME"];
    if (strSheetName.Contains("$") && !strSheetName.Replace("'", "").EndsWith("$"))
    {
      //过滤无效SheetName完毕....
      continue;
    }
    if (lstSheetNames != null && !lstSheetNames.Contains(strSheetName))
      lstSheetNames.Add(strSheetName);
  }

因为读取出来无效SheetName一般情况最后一个字符都不会是$。如果SheetName有一些特殊符号,读取出来的SheetName会自动加上单引号。比如在Excel中将SheetName编辑成MySheet(1),此时读取出来的SheetName就为:'MySheet(1)$',所以判断最后一个字符是不是$之前最好过滤一下单引号。

---------------------------------

(2)Com组件的方式(通过添加 Microsoft.Office.Interop.Excel引用实现)

优点:能够非常灵活的读取Excel中的数据,用户可以灵活的调用各种函数进行处理。

缺点:基于单元格的处理,读取速度较慢,对于数据量较大的文件最好不要使用此种方式读取。

需要添加相应的DLL引用,必须存在此引用才可使用,如果是Web站点部署在IIS上时,还需要服务器机子已安装了Excel,有时候还需要为配置IIS权限。

读取代码如下:

 private Stopwatch wath = new Stopwatch();
 /// <summary>
 /// 使用COM读取Excel
 /// </summary>
 /// <param name="excelFilePath">路径</param>
 /// <returns>DataTabel</returns>
 public System.Data.DataTable GetExcelData(string excelFilePath)
 {
   Excel.Application app = new Excel.Application();
   Excel.Sheets sheets;
   Excel.Workbook workbook = null;
   object oMissiong = System.Reflection.Missing.Value;
   System.Data.DataTable dt = new System.Data.DataTable();
   wath.Start();
   try
   {
     if (app == null)
     {
       return null;
     }
     workbook = app.Workbooks.Open(excelFilePath, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong,
       oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong);
     //将数据读入到DataTable中——Start
     sheets = workbook.Worksheets;
     Excel.Worksheet worksheet = (Excel.Worksheet)sheets.get_Item(1);//读取第一张表
     if (worksheet == null)
       return null;
     string cellContent;
     int iRowCount = worksheet.UsedRange.Rows.Count;
     int iColCount = worksheet.UsedRange.Columns.Count;
     Excel.Range range;
     //负责列头Start
     DataColumn dc;
     int ColumnID = 1;
     range = (Excel.Range)worksheet.Cells[1, 1];
     while (range.Text.ToString().Trim() != "")
     {
       dc = new DataColumn();
       dc.DataType = System.Type.GetType("System.String");
       dc.ColumnName = range.Text.ToString().Trim();
       dt.Columns.Add(dc);

       range = (Excel.Range)worksheet.Cells[1, ++ColumnID];
     }
     //End
     for (int iRow = 2; iRow <= iRowCount; iRow++)
     {
       DataRow dr = dt.NewRow();
       for (int iCol = 1; iCol <= iColCount; iCol++)
       {
         range = (Excel.Range)worksheet.Cells[iRow, iCol];
         cellContent = (range.Value2 == null) ? "" : range.Text.ToString();
           dr[iCol - 1] = cellContent;
       }
       dt.Rows.Add(dr);
     }
     wath.Stop();
     TimeSpan ts = wath.Elapsed;
     //将数据读入到DataTable中——End
     return dt;
   }
   catch
   {
     return null;
   }
   finally
   {
     workbook.Close(false, oMissiong, oMissiong);
     System.Runtime.InteropServices.Marshal.ReleaseComObject(workbook);
     workbook = null;
     app.Workbooks.Close();
     app.Quit();
     System.Runtime.InteropServices.Marshal.ReleaseComObject(app);
     app = null;
     GC.Collect();
     GC.WaitForPendingFinalizers();
   }
 }
 /// <summary>
 /// 使用COM,多线程读取Excel(1 主线程、4 副线程)
 /// </summary>
 /// <param name="excelFilePath">路径</param>
 /// <returns>DataTabel</returns>
 public System.Data.DataTable ThreadReadExcel(string excelFilePath)
 {
   Excel.Application app = new Excel.Application();
   Excel.Sheets sheets = null;
   Excel.Workbook workbook = null;
   object oMissiong = System.Reflection.Missing.Value;
   System.Data.DataTable dt = new System.Data.DataTable();
   wath.Start();
   try
   {
     if (app == null)
     {
       return null;
     }
     workbook = app.Workbooks.Open(excelFilePath, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong,
       oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong);
     //将数据读入到DataTable中——Start
     sheets = workbook.Worksheets;
     Excel.Worksheet worksheet = (Excel.Worksheet)sheets.get_Item(1);//读取第一张表
     if (worksheet == null)
       return null;
     string cellContent;
     int iRowCount = worksheet.UsedRange.Rows.Count;
     int iColCount = worksheet.UsedRange.Columns.Count;
     Excel.Range range;
     //负责列头Start
     DataColumn dc;
     int ColumnID = 1;
     range = (Excel.Range)worksheet.Cells[1, 1];
     while (iColCount >= ColumnID)
     {
       dc = new DataColumn();
       dc.DataType = System.Type.GetType("System.String");
       string strNewColumnName = range.Text.ToString().Trim();
       if (strNewColumnName.Length == 0) strNewColumnName = "_1";
       //判断列名是否重复
       for (int i = 1; i < ColumnID; i++)
       {
         if (dt.Columns[i - 1].ColumnName == strNewColumnName)
           strNewColumnName = strNewColumnName + "_1";
       }
       dc.ColumnName = strNewColumnName;
       dt.Columns.Add(dc);
       range = (Excel.Range)worksheet.Cells[1, ++ColumnID];
     }
     //End
     //数据大于500条,使用多进程进行读取数据
     if (iRowCount - 1 > 500)
     {
       //开始多线程读取数据
       //新建线程
       int b2 = (iRowCount - 1) / 10;
       DataTable dt1 = new DataTable("dt1");
       dt1 = dt.Clone();
       SheetOptions sheet1thread = new SheetOptions(worksheet, iColCount, 2, b2 + 1, dt1);
       Thread othread1 = new Thread(new ThreadStart(sheet1thread.SheetToDataTable));
       othread1.Start();
       //阻塞 1 毫秒,保证第一个读取 dt1
       Thread.Sleep(1);
       DataTable dt2 = new DataTable("dt2");
       dt2 = dt.Clone();
       SheetOptions sheet2thread = new SheetOptions(worksheet, iColCount, b2 + 2, b2 * 2 + 1, dt2);
       Thread othread2 = new Thread(new ThreadStart(sheet2thread.SheetToDataTable));
       othread2.Start();
       DataTable dt3 = new DataTable("dt3");
       dt3 = dt.Clone();
       SheetOptions sheet3thread = new SheetOptions(worksheet, iColCount, b2 * 2 + 2, b2 * 3 + 1, dt3);
       Thread othread3 = new Thread(new ThreadStart(sheet3thread.SheetToDataTable));
       othread3.Start();
       DataTable dt4 = new DataTable("dt4");
       dt4 = dt.Clone();
       SheetOptions sheet4thread = new SheetOptions(worksheet, iColCount, b2 * 3 + 2, b2 * 4 + 1, dt4);
       Thread othread4 = new Thread(new ThreadStart(sheet4thread.SheetToDataTable));
       othread4.Start();
       //主线程读取剩余数据
       for (int iRow = b2 * 4 + 2; iRow <= iRowCount; iRow++)
       {
         DataRow dr = dt.NewRow();
         for (int iCol = 1; iCol <= iColCount; iCol++)
         {
           range = (Excel.Range)worksheet.Cells[iRow, iCol];
           cellContent = (range.Value2 == null) ? "" : range.Text.ToString();
           dr[iCol - 1] = cellContent;
         }
         dt.Rows.Add(dr);
       }
       othread1.Join();
       othread2.Join();
       othread3.Join();
       othread4.Join();
       //将多个线程读取出来的数据追加至 dt1 后面
       foreach (DataRow dr in dt.Rows)
         dt1.Rows.Add(dr.ItemArray);
       dt.Clear();
       dt.Dispose();
       foreach (DataRow dr in dt2.Rows)
         dt1.Rows.Add(dr.ItemArray);
       dt2.Clear();
       dt2.Dispose();
       foreach (DataRow dr in dt3.Rows)
         dt1.Rows.Add(dr.ItemArray);
       dt3.Clear();
       dt3.Dispose();
       foreach (DataRow dr in dt4.Rows)
         dt1.Rows.Add(dr.ItemArray);
       dt4.Clear();
       dt4.Dispose();
       return dt1;
     }
     else
     {
       for (int iRow = 2; iRow <= iRowCount; iRow++)
       {
         DataRow dr = dt.NewRow();
         for (int iCol = 1; iCol <= iColCount; iCol++)
         {
           range = (Excel.Range)worksheet.Cells[iRow, iCol];
           cellContent = (range.Value2 == null) ? "" : range.Text.ToString();
           dr[iCol - 1] = cellContent;
         }
         dt.Rows.Add(dr);
       }
     }
     wath.Stop();
     TimeSpan ts = wath.Elapsed;
     //将数据读入到DataTable中——End
     return dt;
   }
   catch
   {
     return null;
   }
   finally
   {
     workbook.Close(false, oMissiong, oMissiong);
     System.Runtime.InteropServices.Marshal.ReleaseComObject(workbook);
     System.Runtime.InteropServices.Marshal.ReleaseComObject(sheets);
     workbook = null;
     app.Workbooks.Close();
     app.Quit();
     System.Runtime.InteropServices.Marshal.ReleaseComObject(app);
     app = null;
     GC.Collect();
     GC.WaitForPendingFinalizers();
   }
 }

(3)NPOI方式读取Excel(此方法未经过测试)

NPOI 是 POI 项目的 .NET 版本。POI是一个开源的Java读写Excel、WORD等微软OLE2组件文档的项目。使用 NPOI 你就可以在没有安装 Office 或者相应环境的机器上对 WORD/EXCEL 文档进行读写。

优点:读取Excel速度较快,读取方式操作灵活性

缺点:需要下载相应的插件并添加到系统引用当中。

 /// <summary>
 /// 将excel中的数据导入到DataTable中
 /// </summary>
 /// <param name="sheetName">excel工作薄sheet的名称</param>
 /// <param name="isFirstRowColumn">第一行是否是DataTable的列名</param>
 /// <returns>返回的DataTable</returns>
 public DataTable ExcelToDataTable(string sheetName, bool isFirstRowColumn)
 {
   ISheet sheet = null;
   DataTable data = new DataTable();
   int startRow = 0;
   try
   {
     fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
     if (fileName.IndexOf(".xlsx") > 0) // 2007版本
       workbook = new XSSFWorkbook(fs);
     else if (fileName.IndexOf(".xls") > 0) // 2003版本
       workbook = new HSSFWorkbook(fs);
     if (sheetName != null)
     {
       sheet = workbook.GetSheet(sheetName);
     }
     else
     {
       sheet = workbook.GetSheetAt(0);
     }
     if (sheet != null)
     {
       IRow firstRow = sheet.GetRow(0);
       int cellCount = firstRow.LastCellNum; //一行最后一个cell的编号 即总的列数
       if (isFirstRowColumn)
       {
         for (int i = firstRow.FirstCellNum; i < cellCount; ++i)
         {
           DataColumn column = new DataColumn(firstRow.GetCell(i).StringCellValue);
           data.Columns.Add(column);
         }
         startRow = sheet.FirstRowNum + 1;
       }
       else
       {
         startRow = sheet.FirstRowNum;
       }
       //最后一列的标号
       int rowCount = sheet.LastRowNum;
       for (int i = startRow; i <= rowCount; ++i)
       {
         IRow row = sheet.GetRow(i);
         if (row == null) continue; //没有数据的行默认是null       

         DataRow dataRow = data.NewRow();
         for (int j = row.FirstCellNum; j < cellCount; ++j)
         {
           if (row.GetCell(j) != null) //同理,没有数据的单元格都默认是null
             dataRow[j] = row.GetCell(j).ToString();
         }
         data.Rows.Add(dataRow);
       }
     }
     return data;
   }
   catch (Exception ex)
   {
     Console.WriteLine("Exception: " + ex.Message);
     return null;
   }
 }

下面是一些相关的文章,大家可以参考下

(0)

相关推荐

  • C#基于COM方式读取Excel表格的方法

    本文实例讲述了C#基于COM方式读取Excel表格的方法.分享给大家供大家参考,具体如下: using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Data; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using Sys

  • C#实现Excel表数据导入Sql Server数据库中的方法

    本文实例讲述了C#实现Excel表数据导入Sql Server数据库中的方法.分享给大家供大家参考,具体如下: Excel表数据导入Sql Server数据库的方法很多,这里只是介绍了其中一种: 1.首先,我们要先在test数据库中新建一个my_test表,该表具有三个字段tid int类型, tname nvarchar类型, tt nvarchar类型 (注意:my_test表中的数据类型必须与Excel中相应字段的类型一致) 2. 我们用SELECT * FROM  OPENROWSET(

  • C#窗体读取EXCEL并存入SQL数据库的方法

    本文实例讲述了C#窗体读取EXCEL并存入SQL数据库的方法.分享给大家供大家参考.具体实现方法如下: windows窗体上放了一个Textbox1,2个按钮button1和button2~按button1选择excel文件~按button2进行相关处理 复制代码 代码如下: private  void button1_click(object sendeer,EventArgs e) {  OpenFileDialog  openFiledialog1=new OpenFileDialog()

  • C#实现Excel导入sqlite的方法

    本文实例讲述了C#实现Excel导入sqlite的方法,是非常实用的技巧.分享给大家供大家参考.具体方法如下: 首先需要引用system.date.sqlite 具体实现代码如下: system.date.sqlite system.date.sqlite.linq //导入--Excel导入sqlite private void button2_Click(object sender, EventArgs e) { DAL.Sqlite da = new DAL.Sqlite("DataByE

  • c#生成excel示例sql数据库导出excel

    复制代码 代码如下: using System;using System.Collections.Generic;using System.Linq;using System.Text;using Microsoft.Office.Interop.Excel;using System.Reflection; namespace ListToExcel{    class Program    {        static List<objtype> objs = new List<ob

  • ASP.NET(C#)读取Excel的文件内容

    .xls格式       Office2003及以下版本 .xlsx格式 Office2007 及以上版本 .csv格式       以逗号分隔的字符串文本(可以将上述两种文件类型另存为此格式) 读取前两种格式和读取后一种格式会用两种不同的方法. 下面看程序:页面前台: 复制代码 代码如下: <div>       <%-- 文件上传控件  用于将要读取的文件上传 并通过此控件获取文件的信息--%>      <asp:FileUpload ID="fileSele

  • C#怎样才能将XML文件导入SQL Server

    问:怎样才能将XML文件导入SQL Server 2000? 答:将XML文件导入SQL Server有若干种方法,这里提供其中的3种: 大容量装载COM接口.如果需要将文档的实体和属性析取到关系表中,最快的方法就是使用SQL Server 2000 Extensible Markup Language 3.0 Service Pack 1(SQLXML 3.0 SP1)提供的大容量装载COM接口.大容量状态COM接口包含在SQLXML 3.0 SP1的免费下载中. textcopy.exe命令

  • C#将Sql数据保存到Excel文件中的方法

    本文实例讲述了C#将Sql数据保存到Excel文件中的方法,非常有实用价值.分享给大家供大家参考借鉴之用. 具体功能代码如下: public string ExportExcel( DataSet ds,string saveFileName) { try { if (ds == null) return "数据库为空"; bool fileSaved = false; Microsoft.Office.Interop.Excel.Application xlApp = new Mic

  • C#实现把txt文本数据快速读取到excel中

    今天预实现一功能,将txt中的数据转到excel表中,做为matlab的数据源.搜集一些c#操作excel的程序.步骤如下: 下载一个Microsoft.Office.Interop.Excel.dll   在项目中引用. 编写代码如下: string path = "c://date//xyu.txt"; StreamReader sr = new StreamReader(path); string strLine = sr.ReadLine(); int rowNum = 1;

  • C#使用Aspose.Cells控件读取Excel

    Aspose是一个很强大的控件,可以用来操作word,excel,ppt等文件,用这个控件来导入.导出数据非常方便.其中Aspose.Cells就是用来操作Excel的,功能有很多.我所用的是最基本的功能,读取Excel的数据并导入到Dataset或数据库中.读取Excel表格数据的代码如下: 首先要引入命名空间:using Aspose.Cells; 复制代码 代码如下: Workbook workbook = new Workbook(); workbook.Open("C:\\test.x

随机推荐