关于.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;
        }
    }
}

  • 比OLEDB快?最好支持这一主张。
  • 斯汀吉杰克:我不怪你怀疑。我已经用代码编辑了我的响应,这表明电子表格设备确实比OLEDB更快。
  • 我的数字有些不同,但相当接近。你真的应该把它贴在你的网站上。当你四处寻找一个XLS组件的时候,这真的是一个值得一看的信息。
  • 我们抵制将此类基准放在我们的网站上的冲动,因为最重要的基准是它在实际应用中的工作速度,所以我们在本页右侧有来自真实客户的报价:spreadsheetgear.com/products/spreadsheetgear.net.aspx
  • 我过去广泛使用电子表格工具,并做了一些比较测试:它比任何其他竞争对手或本地方法都快得多。在我看来,最好的电子表格组件。


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


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

  • 使用interop库。LL是对的。


我建议您尝试使用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

  • 马的嘴需要一段时间来咀嚼,所以对于大的(>1000行)文件来说,这不会太好。
  • 另外,如果这是一个服务器环境,那么在服务器环境中马嘴也不会运行得很好。
  • 是的,由于速度问题,我想避免使用COM或Interop路由;但这可能是我们必须做的。在我走那条路之前还有其他想法吗?
  • 啊,都很好。我的解决办法很差。
  • 不是穷人。这可能是唯一的解决办法。


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

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

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

  • 文件当前有标题行。即使我告诉OLEDB包含标题行(使用hdr=no),它仍然只返回12列并跳过第一列。
  • HDR选项听起来倒过来了…检查(connectionstrings.com/excel)作为conn字符串的引用。
  • 我知道它听起来是向后的,但是你设置hdr=no让它给你标题行(基本上,你是说标题行是一个数据行)
  • 啊…好啊。我明白了。你也试过关闭IMEX吗?
  • 你可以…尝试使用OLEDB进行读取,如果您有12列,请使用COM对象将一个不受影响的值插入第一列的几行中。当您重新读取ss时,非影响值可能会被删除。有点老土,但这样你就不必用com-obj阅读所有内容。