好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

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 ;

   }

}

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

dy("nrwz");

查看更多关于C#读取Excel的三种方式以及比较分析的详细内容...

  阅读:75次