首页 后端开发 C#.Net教程 C#读取Excel的三种方式以及比较分析

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

Jan 13, 2017 pm 05:10 PM

(1)OleDB方式

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

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

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

读取代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

//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("&#39;", "").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权限。

读取代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

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速度较快,读取方式操作灵活性

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

/// <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;

  }

}

登录后复制

更多C#读取Excel的三种方式以及比较分析相关文章请关注PHP中文网!


本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

热门话题

Java教程
1662
14
CakePHP 教程
1419
52
Laravel 教程
1313
25
PHP教程
1262
29
C# 教程
1235
24
C#.NET:探索核心概念和编程基础知识 C#.NET:探索核心概念和编程基础知识 Apr 10, 2025 am 09:32 AM

C#是一种现代、面向对象的编程语言,由微软开发并作为.NET框架的一部分。1.C#支持面向对象编程(OOP),包括封装、继承和多态。2.C#中的异步编程通过async和await关键字实现,提高应用的响应性。3.使用LINQ可以简洁地处理数据集合。4.常见错误包括空引用异常和索引超出范围异常,调试技巧包括使用调试器和异常处理。5.性能优化包括使用StringBuilder和避免不必要的装箱和拆箱。

测试C#.NET应用程序:单元,集成和端到端测试 测试C#.NET应用程序:单元,集成和端到端测试 Apr 09, 2025 am 12:04 AM

C#.NET应用的测试策略包括单元测试、集成测试和端到端测试。1.单元测试确保代码的最小单元独立工作,使用MSTest、NUnit或xUnit框架。2.集成测试验证多个单元组合的功能,常用模拟数据和外部服务。3.端到端测试模拟用户完整操作流程,通常使用Selenium进行自动化测试。

c#.net的持续相关性:查看当前用法 c#.net的持续相关性:查看当前用法 Apr 16, 2025 am 12:07 AM

C#.NET依然重要,因为它提供了强大的工具和库,支持多种应用开发。1)C#结合.NET框架,使开发高效便捷。2)C#的类型安全和垃圾回收机制增强了其优势。3).NET提供跨平台运行环境和丰富的API,提升了开发灵活性。

从网络到桌面:C#.NET的多功能性 从网络到桌面:C#.NET的多功能性 Apr 15, 2025 am 12:07 AM

C#.NETisversatileforbothwebanddesktopdevelopment.1)Forweb,useASP.NETfordynamicapplications.2)Fordesktop,employWindowsFormsorWPFforrichinterfaces.3)UseXamarinforcross-platformdevelopment,enablingcodesharingacrossWindows,macOS,Linux,andmobiledevices.

高级C#.NET教程:ACE您的下一次高级开发人员面试 高级C#.NET教程:ACE您的下一次高级开发人员面试 Apr 08, 2025 am 12:06 AM

C#高级开发者面试需要掌握异步编程、LINQ、.NET框架内部工作原理等核心知识。1.异步编程通过async和await简化操作,提升应用响应性。2.LINQ以SQL风格操作数据,需注意性能。3..NET框架的CLR管理内存,垃圾回收需谨慎使用。

c#.net适合您吗?评估其适用性 c#.net适合您吗?评估其适用性 Apr 13, 2025 am 12:03 AM

c#.netissutableforenterprise-levelapplications withemofrosoftecosystemdueToItsStrongTyping,richlibraries,androbustperraries,androbustperformance.however,itmaynotbeidealfoross-platement forment forment forment forvepentment offependment dovelopment toveloperment toveloperment whenrawspeedsportor whenrawspeedseedpolitical politionalitable,

.NET中的C#代码:探索编程过程 .NET中的C#代码:探索编程过程 Apr 12, 2025 am 12:02 AM

C#在.NET中的编程过程包括以下步骤:1)编写C#代码,2)编译为中间语言(IL),3)由.NET运行时(CLR)执行。C#在.NET中的优势在于其现代化语法、强大的类型系统和与.NET框架的紧密集成,适用于从桌面应用到Web服务的各种开发场景。

C#作为多功能.NET语言:应用程序和示例 C#作为多功能.NET语言:应用程序和示例 Apr 26, 2025 am 12:26 AM

C#在企业级应用、游戏开发、移动应用和Web开发中均有广泛应用。1)在企业级应用中,C#常用于ASP.NETCore开发WebAPI。2)在游戏开发中,C#与Unity引擎结合,实现角色控制等功能。3)C#支持多态性和异步编程,提高代码灵活性和应用性能。

See all articles