首页 > 编程 > C# > 正文

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

2019-10-29 21:36:32
字体:
来源:转载
供稿:网友

这篇文章主要介绍了C#读取Excel的三种方式以及比较分析,需要的朋友可以参考下

(1)OleDB方式

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

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

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

读取代码如下:

 

 
  1. public DataTable GetExcelTableByOleDB(string strExcelPath, string tableName) 
  2. try 
  3. DataTable dtExcel = new DataTable(); 
  4. //数据表 
  5. DataSet ds = new DataSet(); 
  6. //获取文件扩展名 
  7. string strExtension = System.IO.Path.GetExtension(strExcelPath); 
  8. string strFileName = System.IO.Path.GetFileName(strExcelPath); 
  9. //Excel的连接 
  10. OleDbConnection objConn = null
  11. switch (strExtension) 
  12. case ".xls"
  13. objConn = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + strExcelPath + ";" + "Extended Properties=/"Excel 8.0;HDR=NO;IMEX=1;/""); 
  14. break
  15. case ".xlsx"
  16. objConn = new OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + strExcelPath + ";" + "Extended Properties=/"Excel 12.0;HDR=NO;IMEX=1;/""); 
  17. break
  18. default
  19. objConn = null
  20. break
  21. if (objConn == null
  22. return null
  23. objConn.Open(); 
  24. //获取Excel中所有Sheet表的信息 
  25. //System.Data.DataTable schemaTable = objConn.GetOleDbSchemaTable(System.Data.OleDb.OleDbSchemaGuid.Tables, null); 
  26. //获取Excel的第一个Sheet表名 
  27. //string tableName = schemaTable.Rows[0][2].ToString().Trim(); 
  28. string strSql = "select * from [" + tableName + "]"
  29. //获取Excel指定Sheet表中的信息 
  30. OleDbCommand objCmd = new OleDbCommand(strSql, objConn); 
  31. OleDbDataAdapter myData = new OleDbDataAdapter(strSql, objConn); 
  32. myData.Fill(ds, tableName);//填充数据 
  33. objConn.Close(); 
  34. //dtExcel即为excel文件中指定表中存储的信息 
  35. dtExcel = ds.Tables[tableName]; 
  36. return dtExcel; 
  37. catch 
  38. 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做以上检查。下面给出了过滤的方案。(此问题我们有验证过,大家自己验证一下吧)

 

 
  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. string strSheetName = (string)dtSheetName.Rows[i]["TABLE_NAME"]; 
  6. if (strSheetName.Contains("$") && !strSheetName.Replace("'""").EndsWith("$")) 
  7. //过滤无效SheetName完毕.... 
  8. continue
  9. if (lstSheetNames != null && !lstSheetNames.Contains(strSheetName)) 
  10. 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权限。

读取代码如下:

 

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

(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. ISheet sheet = null
  9. DataTable data = new DataTable(); 
  10. int startRow = 0; 
  11. try 
  12. fs = new FileStream(fileName, FileMode.Open, FileAccess.Read); 
  13. if (fileName.IndexOf(".xlsx") > 0) // 2007版本 
  14. workbook = new XSSFWorkbook(fs); 
  15. else if (fileName.IndexOf(".xls") > 0) // 2003版本 
  16. workbook = new HSSFWorkbook(fs); 
  17. if (sheetName != null
  18. sheet = workbook.GetSheet(sheetName); 
  19. else 
  20. sheet = workbook.GetSheetAt(0); 
  21. if (sheet != null
  22. IRow firstRow = sheet.GetRow(0); 
  23. int cellCount = firstRow.LastCellNum; //一行最后一个cell的编号 即总的列数 
  24. if (isFirstRowColumn) 
  25. for (int i = firstRow.FirstCellNum; i < cellCount; ++i) 
  26. DataColumn column = new DataColumn(firstRow.GetCell(i).StringCellValue); 
  27. data.Columns.Add(column); 
  28. startRow = sheet.FirstRowNum + 1; 
  29. else 
  30. startRow = sheet.FirstRowNum; 
  31. //最后一列的标号 
  32. int rowCount = sheet.LastRowNum; 
  33. for (int i = startRow; i <= rowCount; ++i) 
  34. IRow row = sheet.GetRow(i); 
  35. if (row == nullcontinue//没有数据的行默认是null        
  36.  
  37. DataRow dataRow = data.NewRow(); 
  38. for (int j = row.FirstCellNum; j < cellCount; ++j) 
  39. if (row.GetCell(j) != null//同理,没有数据的单元格都默认是null 
  40. dataRow[j] = row.GetCell(j).ToString(); 
  41. data.Rows.Add(dataRow); 
  42. return data; 
  43. catch (Exception ex) 
  44. Console.WriteLine("Exception: " + ex.Message); 
  45. return null

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


注:相关教程知识阅读请移步到c#教程频道。
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表