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


    (1)OleDB方式

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

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

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

    读取代码如下:

       1: public DataTable GetExcelTableByOleDB(string strExcelPath, string tableName)
       2: {
       3:     try
       4:     {
       5:         DataTable dtExcel = new DataTable();
       6:         //数据表
       7:         DataSet ds = new DataSet();
       8:         //获取文件扩展名
       9:         string strExtension = System.IO.Path.GetExtension(strExcelPath);
      10:         string strFileName = System.IO.Path.GetFileName(strExcelPath);
      11:         //Excel的连接
      12:         OleDbConnection objConn = null;
      13:         switch (strExtension)
      14:         {
      15:             case ".xls":
      16:                 objConn = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + strExcelPath + ";" + "Extended Properties="Excel 8.0;HDR=NO;IMEX=1;"");
      17:                 break;
      18:             case ".xlsx":
      19:                 objConn = new OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + strExcelPath + ";" + "Extended Properties="Excel 12.0;HDR=NO;IMEX=1;"");
      20:                 break;
      21:             default:
      22:                 objConn = null;
      23:                 break;
      24:         }
      25:         if (objConn == null)
      26:         {
      27:             return null;
      28:         }
      29:         objConn.Open();
      30:         //获取Excel中所有Sheet表的信息
      31:         //System.Data.DataTable schemaTable = objConn.GetOleDbSchemaTable(System.Data.OleDb.OleDbSchemaGuid.Tables, null);
      32:         //获取Excel的第一个Sheet表名
      33:         //string tableName = schemaTable.Rows[0][2].ToString().Trim();
      34:         string strSql = "select * from [" + tableName + "]";
      35:         //获取Excel指定Sheet表中的信息
      36:         OleDbCommand objCmd = new OleDbCommand(strSql, objConn);
      37:         OleDbDataAdapter myData = new OleDbDataAdapter(strSql, objConn);
      38:         myData.Fill(ds, tableName);//填充数据
      39:         objConn.Close();
      40:         //dtExcel即为excel文件中指定表中存储的信息
      41:         dtExcel = ds.Tables[tableName];
      42:         return dtExcel;
      43:     }
      44:     catch
      45:     {
      46:         return null;
      47:     }
      48: }

    下面说明一下连接字符串

            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做以上检查。下面给出了过滤的方案。(此问题我们有验证过,大家自己验证一下吧)

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

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

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

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

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

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

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

    读取代码如下:

       1: private Stopwatch wath = new Stopwatch();
       2: /// <summary>
       3: /// 使用COM读取Excel
       4: /// </summary>
       5: /// <param name="excelFilePath">路径</param>
       6: /// <returns>DataTabel</returns>
       7: public System.Data.DataTable GetExcelData(string excelFilePath)
       8: {
       9:     Excel.Application app = new Excel.Application();
      10:     Excel.Sheets sheets;
      11:     Excel.Workbook workbook = null;
      12:     object oMissiong = System.Reflection.Missing.Value;
      13:     System.Data.DataTable dt = new System.Data.DataTable();
      14:     wath.Start();
      15:     try
      16:     {
      17:         if (app == null)
      18:         {
      19:             return null;
      20:         }
      21:         workbook = app.Workbooks.Open(excelFilePath, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, 
      22:             oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong);
      23:         //将数据读入到DataTable中——Start   
      24:         sheets = workbook.Worksheets;
      25:         Excel.Worksheet worksheet = (Excel.Worksheet)sheets.get_Item(1);//读取第一张表
      26:         if (worksheet == null)
      27:             return null;
      28:         string cellContent;
      29:         int iRowCount = worksheet.UsedRange.Rows.Count;
      30:         int iColCount = worksheet.UsedRange.Columns.Count;
      31:         Excel.Range range;
      32:         //负责列头Start
      33:         DataColumn dc;
      34:         int ColumnID = 1;
      35:         range = (Excel.Range)worksheet.Cells[1, 1];
      36:         while (range.Text.ToString().Trim() != "")
      37:         {
      38:             dc = new DataColumn();
      39:             dc.DataType = System.Type.GetType("System.String");
      40:             dc.ColumnName = range.Text.ToString().Trim();
      41:             dt.Columns.Add(dc);
      42:  
      43:             range = (Excel.Range)worksheet.Cells[1, ++ColumnID];
      44:         }
      45:         //End
      46:         for (int iRow = 2; iRow <= iRowCount; iRow++)
      47:         {
      48:             DataRow dr = dt.NewRow();
      49:             for (int iCol = 1; iCol <= iColCount; iCol++)
      50:             {
      51:                 range = (Excel.Range)worksheet.Cells[iRow, iCol];
      52:                 cellContent = (range.Value2 == null) ? "" : range.Text.ToString();
      53:                     dr[iCol - 1] = cellContent;
      54:             }
      55:             dt.Rows.Add(dr);
      56:         }
      57:         wath.Stop();
      58:         TimeSpan ts = wath.Elapsed;
      59:         //将数据读入到DataTable中——End
      60:         return dt;
      61:     }
      62:     catch
      63:     {
      64:         return null;
      65:     }
      66:     finally
      67:     {
      68:         workbook.Close(false, oMissiong, oMissiong);
      69:         System.Runtime.InteropServices.Marshal.ReleaseComObject(workbook);
      70:         workbook = null;
      71:         app.Workbooks.Close();
      72:         app.Quit();
      73:         System.Runtime.InteropServices.Marshal.ReleaseComObject(app);
      74:         app = null;
      75:         GC.Collect();
      76:         GC.WaitForPendingFinalizers();
      77:     }
      78: }
      79: /// <summary>
      80: /// 使用COM,多线程读取Excel(1 主线程、4 副线程)
      81: /// </summary>
      82: /// <param name="excelFilePath">路径</param>
      83: /// <returns>DataTabel</returns>
      84: public System.Data.DataTable ThreadReadExcel(string excelFilePath)
      85: {
      86:     Excel.Application app = new Excel.Application();
      87:     Excel.Sheets sheets = null;
      88:     Excel.Workbook workbook = null;
      89:     object oMissiong = System.Reflection.Missing.Value;
      90:     System.Data.DataTable dt = new System.Data.DataTable();
      91:     wath.Start();
      92:     try
      93:     {
      94:         if (app == null)
      95:         {
      96:             return null;
      97:         }
      98:         workbook = app.Workbooks.Open(excelFilePath, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, 
      99:             oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong);
     100:         //将数据读入到DataTable中——Start   
     101:         sheets = workbook.Worksheets;
     102:         Excel.Worksheet worksheet = (Excel.Worksheet)sheets.get_Item(1);//读取第一张表
     103:         if (worksheet == null)
     104:             return null;
     105:         string cellContent;
     106:         int iRowCount = worksheet.UsedRange.Rows.Count;
     107:         int iColCount = worksheet.UsedRange.Columns.Count;
     108:         Excel.Range range;
     109:         //负责列头Start
     110:         DataColumn dc;
     111:         int ColumnID = 1;
     112:         range = (Excel.Range)worksheet.Cells[1, 1];
     113:         while (iColCount >= ColumnID)
     114:         {
     115:             dc = new DataColumn();
     116:             dc.DataType = System.Type.GetType("System.String");
     117:             string strNewColumnName = range.Text.ToString().Trim();
     118:             if (strNewColumnName.Length == 0) strNewColumnName = "_1";
     119:             //判断列名是否重复
     120:             for (int i = 1; i < ColumnID; i++)
     121:             {
     122:                 if (dt.Columns[i - 1].ColumnName == strNewColumnName)
     123:                     strNewColumnName = strNewColumnName + "_1";
     124:             }
     125:             dc.ColumnName = strNewColumnName;
     126:             dt.Columns.Add(dc);
     127:             range = (Excel.Range)worksheet.Cells[1, ++ColumnID];
     128:         }
     129:         //End
     130:         //数据大于500条,使用多进程进行读取数据
     131:         if (iRowCount - 1 > 500)
     132:         {
     133:             //开始多线程读取数据
     134:             //新建线程
     135:             int b2 = (iRowCount - 1) / 10;
     136:             DataTable dt1 = new DataTable("dt1");
     137:             dt1 = dt.Clone();
     138:             SheetOptions sheet1thread = new SheetOptions(worksheet, iColCount, 2, b2 + 1, dt1);
     139:             Thread othread1 = new Thread(new ThreadStart(sheet1thread.SheetToDataTable));
     140:             othread1.Start();
     141:             //阻塞 1 毫秒,保证第一个读取 dt1
     142:             Thread.Sleep(1);
     143:             DataTable dt2 = new DataTable("dt2");
     144:             dt2 = dt.Clone();
     145:             SheetOptions sheet2thread = new SheetOptions(worksheet, iColCount, b2 + 2, b2 * 2 + 1, dt2);
     146:             Thread othread2 = new Thread(new ThreadStart(sheet2thread.SheetToDataTable));
     147:             othread2.Start();
     148:             DataTable dt3 = new DataTable("dt3");
     149:             dt3 = dt.Clone();
     150:             SheetOptions sheet3thread = new SheetOptions(worksheet, iColCount, b2 * 2 + 2, b2 * 3 + 1, dt3);
     151:             Thread othread3 = new Thread(new ThreadStart(sheet3thread.SheetToDataTable));
     152:             othread3.Start();
     153:             DataTable dt4 = new DataTable("dt4");
     154:             dt4 = dt.Clone();
     155:             SheetOptions sheet4thread = new SheetOptions(worksheet, iColCount, b2 * 3 + 2, b2 * 4 + 1, dt4);
     156:             Thread othread4 = new Thread(new ThreadStart(sheet4thread.SheetToDataTable));
     157:             othread4.Start();
     158:             //主线程读取剩余数据
     159:             for (int iRow = b2 * 4 + 2; iRow <= iRowCount; iRow++)
     160:             {
     161:                 DataRow dr = dt.NewRow();
     162:                 for (int iCol = 1; iCol <= iColCount; iCol++)
     163:                 {
     164:                     range = (Excel.Range)worksheet.Cells[iRow, iCol];
     165:                     cellContent = (range.Value2 == null) ? "" : range.Text.ToString();
     166:                     dr[iCol - 1] = cellContent;
     167:                 }
     168:                 dt.Rows.Add(dr);
     169:             }
     170:             othread1.Join();
     171:             othread2.Join();
     172:             othread3.Join();
     173:             othread4.Join();
     174:             //将多个线程读取出来的数据追加至 dt1 后面
     175:             foreach (DataRow dr in dt.Rows)
     176:                 dt1.Rows.Add(dr.ItemArray);
     177:             dt.Clear();
     178:             dt.Dispose();
     179:             foreach (DataRow dr in dt2.Rows)
     180:                 dt1.Rows.Add(dr.ItemArray);
     181:             dt2.Clear();
     182:             dt2.Dispose();
     183:             foreach (DataRow dr in dt3.Rows)
     184:                 dt1.Rows.Add(dr.ItemArray);
     185:             dt3.Clear();
     186:             dt3.Dispose();
     187:             foreach (DataRow dr in dt4.Rows)
     188:                 dt1.Rows.Add(dr.ItemArray);
     189:             dt4.Clear();
     190:             dt4.Dispose();
     191:             return dt1;
     192:         }
     193:         else
     194:         {
     195:             for (int iRow = 2; iRow <= iRowCount; iRow++)
     196:             {
     197:                 DataRow dr = dt.NewRow();
     198:                 for (int iCol = 1; iCol <= iColCount; iCol++)
     199:                 {
     200:                     range = (Excel.Range)worksheet.Cells[iRow, iCol];
     201:                     cellContent = (range.Value2 == null) ? "" : range.Text.ToString();
     202:                     dr[iCol - 1] = cellContent;
     203:                 }
     204:                 dt.Rows.Add(dr);
     205:             }
     206:         }
     207:         wath.Stop();
     208:         TimeSpan ts = wath.Elapsed;
     209:         //将数据读入到DataTable中——End
     210:         return dt;
     211:     }
     212:     catch
     213:     {
     214:         return null;
     215:     }
     216:     finally
     217:     {
     218:         workbook.Close(false, oMissiong, oMissiong);
     219:         System.Runtime.InteropServices.Marshal.ReleaseComObject(workbook);
     220:         System.Runtime.InteropServices.Marshal.ReleaseComObject(sheets);
     221:         workbook = null;
     222:         app.Workbooks.Close();
     223:         app.Quit();
     224:         System.Runtime.InteropServices.Marshal.ReleaseComObject(app);
     225:         app = null;
     226:         GC.Collect();
     227:         GC.WaitForPendingFinalizers();
     228:     }
     229: }

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

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

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

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

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

       1: /// <summary>
       2: /// 将excel中的数据导入到DataTable中
       3: /// </summary>
       4: /// <param name="sheetName">excel工作薄sheet的名称</param>
       5: /// <param name="isFirstRowColumn">第一行是否是DataTable的列名</param>
       6: /// <returns>返回的DataTable</returns>
       7: public DataTable ExcelToDataTable(string sheetName, bool isFirstRowColumn)
       8: {
       9:     ISheet sheet = null;
      10:     DataTable data = new DataTable();
      11:     int startRow = 0;
      12:     try
      13:     {
      14:         fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
      15:         if (fileName.IndexOf(".xlsx") > 0) // 2007版本
      16:             workbook = new XSSFWorkbook(fs);
      17:         else if (fileName.IndexOf(".xls") > 0) // 2003版本
      18:             workbook = new HSSFWorkbook(fs);
      19:         if (sheetName != null)
      20:         {
      21:             sheet = workbook.GetSheet(sheetName);
      22:         }
      23:         else
      24:         {
      25:             sheet = workbook.GetSheetAt(0);
      26:         }
      27:         if (sheet != null)
      28:         {
      29:             IRow firstRow = sheet.GetRow(0);
      30:             int cellCount = firstRow.LastCellNum; //一行最后一个cell的编号 即总的列数
      31:             if (isFirstRowColumn)
      32:             {
      33:                 for (int i = firstRow.FirstCellNum; i < cellCount; ++i)
      34:                 {
      35:                     DataColumn column = new DataColumn(firstRow.GetCell(i).StringCellValue);
      36:                     data.Columns.Add(column);
      37:                 }
      38:                 startRow = sheet.FirstRowNum + 1;
      39:             }
      40:             else
      41:             {
      42:                 startRow = sheet.FirstRowNum;
      43:             }
      44:             //最后一列的标号
      45:             int rowCount = sheet.LastRowNum;
      46:             for (int i = startRow; i <= rowCount; ++i)
      47:             {
      48:                 IRow row = sheet.GetRow(i);
      49:                 if (row == null) continue; //没有数据的行默认是null       
      50:                 
      51:                 DataRow dataRow = data.NewRow();
      52:                 for (int j = row.FirstCellNum; j < cellCount; ++j)
      53:                 {
      54:                     if (row.GetCell(j) != null) //同理,没有数据的单元格都默认是null
      55:                         dataRow[j] = row.GetCell(j).ToString();
      56:                 }
      57:                 data.Rows.Add(dataRow);
      58:             }
      59:         }
      60:         return data;
      61:     }
      62:     catch (Exception ex)
      63:     {
      64:         Console.WriteLine("Exception: " + ex.Message);
      65:         return null;
      66:     }
      67: }

    部分内容参考http://www.cnblogs.com/Tsong/archive/2013/02/21/2920941.html。特此说明!

  • 相关阅读:
    Eclipse中配置约束
    c++ 虚函数
    cocos3 menu
    cocos3 封装一个ball
    cocos3 内存管理机制
    cocos3 多文件拆分cocos
    cocos3 labelttf
    cocos3 messagebox
    cocos3 log
    cocos3 director sprite scene之间的关系
  • 原文地址:https://www.cnblogs.com/Jingkunliu/p/4234099.html
Copyright © 2020-2023  润新知