关于.net:如何在不丢失任何列的情况下读取c#中的excel文件?

How do I read an excel file in c# without missing any columns?

我使用OLEDB连接成功读取Excel文件已经有一段时间了,但我遇到了一个问题。我有人试图上传一个Excel电子表格,但在第一列中没有任何内容,当我试图读取该文件时,它无法识别该列。

我当前正在使用以下OLEDB连接字符串:

提供程序=microsoft.jet.oledb.4.0;数据源=c: est.xls;extended properties="Excel 8.0;IMEX=1;"

因此,如果Excel文件中有13列,那么我返回的OLEDBDataReader只有12列/字段。

任何见解都会受到赞赏。


.NET的spreadsheetgear为您提供了使用.NET中的XLS和XLSX工作簿的API。它比OLEDB或Excel COM对象模型更容易使用,速度更快。你可以看到现场样品,也可以自己试用免费试用。

免责声明:我拥有SpreadsheetGear LLC

编辑:

StingyJack评论道:"比OLEDB更快?最好支持这一主张。

这是一个合理的要求。我一直认为我所知道的事实是错误的,所以我不能因为怀疑而责备任何人。

下面是使用电子表格装置创建50000行10列工作簿的代码,将其保存到磁盘,然后使用OLEDB和电子表格装置对数字求和。SpreadsheetGear在0.31秒内读取500K电池,而OLEDB的读取速度仅为0.63秒,仅为OLEDB的两倍多。电子表格设备实际上创建和读取工作簿的时间比使用OLEDB读取工作簿所需的时间要短。

代码如下。您可以自己尝试使用电子表格工具免费试用版。

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
using System;
using System.Data;
using System.Data.OleDb;
using SpreadsheetGear;
using SpreadsheetGear.Advanced.Cells;
using System.Diagnostics;

namespace SpreadsheetGearAndOleDBBenchmark
{
    class Program
    {
        static void Main(string[] args)
        {
            // Warm up (get the code JITed).
            BM(10, 10);

            // Do it for real.
            BM(50000, 10);
        }

        static void BM(int rows, int cols)
        {
            // Compare the performance of OleDB to SpreadsheetGear for reading
            // workbooks. We sum numbers just to have something to do.
            //
            // Run on Windows Vista 32 bit, Visual Studio 2008, Release Build,
            // Run Without Debugger:
            //  Create time: 0.25 seconds
            //  OleDb Time: 0.63 seconds
            //  SpreadsheetGear Time: 0.31 seconds
            //
            // SpreadsheetGear is more than twice as fast at reading. Furthermore,
            // SpreadsheetGear can create the file and read it faster than OleDB
            // can just read it.
            string filename = @"C:\tmp\SpreadsheetGearOleDbBenchmark.xls";
            Console.WriteLine("
Creating {0} rows x {1} columns"
, rows, cols);
            Stopwatch timer = Stopwatch.StartNew();
            double createSum = CreateWorkbook(filename, rows, cols);
            double createTime = timer.Elapsed.TotalSeconds;
            Console.WriteLine("Create sum of {0} took {1} seconds.", createSum, createTime);
            timer = Stopwatch.StartNew();
            double oleDbSum = ReadWithOleDB(filename);
            double oleDbTime = timer.Elapsed.TotalSeconds;
            Console.WriteLine("OleDb sum of {0} took {1} seconds.", oleDbSum, oleDbTime);
            timer = Stopwatch.StartNew();
            double spreadsheetGearSum = ReadWithSpreadsheetGear(filename);
            double spreadsheetGearTime = timer.Elapsed.TotalSeconds;
            Console.WriteLine("SpreadsheetGear sum of {0} took {1} seconds.", spreadsheetGearSum, spreadsheetGearTime);
        }

        static double CreateWorkbook(string filename, int rows, int cols)
        {
            IWorkbook workbook = Factory.GetWorkbook();
            IWorksheet worksheet = workbook.Worksheets[0];
            IValues values = (IValues)worksheet;
            double sum = 0.0;
            Random rand = new Random();
            // Put labels in the first row.
            foreach (IRange cell in worksheet.Cells[0, 0, 0, cols - 1])
                cell.Value ="Cell-" + cell.Address;
            // Using IRange and foreach be less code,
            // but we'll do it the fast way.
            for (int row = 1; row <= rows; row++)
            {
                for (int col = 0; col < cols; col++)
                {
                    double number = rand.NextDouble();
                    sum += number;
                    values.SetNumber(row, col, number);
                }
            }
            workbook.SaveAs(filename, FileFormat.Excel8);
            return sum;
        }

        static double ReadWithSpreadsheetGear(string filename)
        {
            IWorkbook workbook = Factory.GetWorkbook(filename);
            IWorksheet worksheet = workbook.Worksheets[0];
            IValues values = (IValues)worksheet;
            IRange usedRahge = worksheet.UsedRange;
            int rowCount = usedRahge.RowCount;
            int colCount = usedRahge.ColumnCount;
            double sum = 0.0;
            // We could use foreach (IRange cell in usedRange) for cleaner
            // code, but this is faster.
            for (int row = 1; row <= rowCount; row++)
            {
                for (int col = 0; col < colCount; col++)
                {
                    IValue value = values[row, col];
                    if (value != null && value.Type == SpreadsheetGear.Advanced.Cells.ValueType.Number)
                        sum += value.Number;
                }
            }
            return sum;
        }

        static double ReadWithOleDB(string filename)
        {
            String connectionString =  
               "Provider=Microsoft.Jet.OLEDB.4.0;" +
               "Data Source=" + filename +";" +
               "Extended Properties=Excel 8.0;";
            OleDbConnection connection = new OleDbConnection(connectionString);
            connection.Open();
            OleDbCommand selectCommand =new OleDbCommand("SELECT * FROM [Sheet1$]", connection);
            OleDbDataAdapter dataAdapter = new OleDbDataAdapter();
            dataAdapter.SelectCommand = selectCommand;
            DataSet dataSet = new DataSet();
            dataAdapter.Fill(dataSet);
            connection.Close();
            double sum = 0.0;
            // We'll make some assumptions for brevity of the code.
            DataTable dataTable = dataSet.Tables[0];
            int cols = dataTable.Columns.Count;
            foreach (DataRow row in dataTable.Rows)
            {
                for (int i = 0; i < cols; i++)
                {
                    object val = row[i];
                    if (val is double)
                        sum += (double)val;
                }
            }
            return sum;
        }
    }
}


你也许可以看看ExcelMapper。它是将Excel文件作为强类型对象读取的工具。它隐藏了从代码中读取Excel的所有细节。如果您的Excel缺少列或列中缺少数据,则需要注意。你读了你感兴趣的数据。您可以从http://code.google.com/p/excelmapper/获取ExcelMapper的代码/可执行文件。


我们总是使用ExcelInterop打开电子表格并直接分析(例如,类似于在VBA中扫描单元格的方式),或者创建锁定的模板,强制在用户保存数据之前填写某些列。


我建议您尝试使用Visual Studio Tools for Office和Excel Interop!它的使用非常简单。


您可以尝试使用Excel和COM。这样,你就可以直接从马嘴里得到信息了。

来自D.Anand,在msdn论坛上:

在项目中创建到Excel对象库的引用。Excel对象库可以添加到"添加引用"对话框的"COM"选项卡中。

以下是有关C中Excel对象模型的一些信息#http://msdn.microsoft.com/en-us/library/aa168292(office.11).aspx


如果需要Excel工作表的格式具有列标题,那么您将始终拥有13列。处理时只需要跳过标题行。

这还可以纠正用户按您不期望的顺序放置列的情况。(在标题行中检测列索引并进行适当的读取)

我看到其他人推荐ExcelInterop,但是与OLEDB相比,Jeez是一个缓慢的选择。此外,还需要在服务器上安装Excel或OWC(授权)。