从C#读取Excel文件

232

有没有一个免费或开源的库可以直接从C#程序读取Excel文件(.xls)?

它不需要太花哨,只需要选择一个工作表并将数据作为字符串读取。到目前为止,我一直在使用Excel的导出Unicode文本功能,并解析生成的(制表符分隔的)文件,但我想消除手动步骤。

32个回答

152
var fileName = string.Format("{0}\\fileNameHere", Directory.GetCurrentDirectory());
var connectionString = string.Format("Provider=Microsoft.Jet.OLEDB.4.0; data source={0}; Extended Properties=Excel 8.0;", fileName);

var adapter = new OleDbDataAdapter("SELECT * FROM [workSheetNameHere$]", connectionString);
var ds = new DataSet();

adapter.Fill(ds, "anyNameHere");

DataTable data = ds.Tables["anyNameHere"];

这是我通常使用的方法。略有不同的是,我通常在表格末尾加上一个 AsEnumerable():

var data = ds.Tables["anyNameHere"].AsEnumerable();

这让我可以使用LINQ来搜索和从字段构建结构体。

var query = data.Where(x => x.Field<string>("phoneNumber") != string.Empty).Select(x =>
                new MyContact
                    {
                        firstName= x.Field<string>("First Name"),
                        lastName = x.Field<string>("Last Name"),
                        phoneNumber =x.Field<string>("Phone Number"),
                    });

1
刚在 MSDN 上查了一下。看起来 <T> 只是用于尝试将列中的内容转换为某种类型。在这个例子中,只是将列中的数据转换为字符串。如果你想要一个 double 类型,你需要调用 double.Parse(x.Field<string>("Cost")) 或类似的方法。Field 是 DataRow 的扩展方法,似乎没有非泛型版本。 - Robin Robinson
将double.Parse添加到Linq查询中会严重降低其速度吗? - Anonymous Type
23
请注意,如果您正在读取 xlsx 文件,则需要使用以下连接字符串:string.Format("Provider=Microsoft.ACE.OLEDB.12.0;Data Source={0}; Extended Properties=Excel 12.0;", fileName) - Andreas Grech
7
遗憾的是,Jet.OLEDB驱动程序不支持64位;如果您仍然想继续使用此方法,则需要切换到x86目标而不是Any CPU。或者安装64位ACE驱动程序,并更改连接字符串以使用该驱动程序(如Andreas所示)-http://www.microsoft.com/en-us/download/details.aspx?displaylang=en&id=13255。 - Duncan
如果目标计算机安装了32位版本的Office,则无法安装64位ACE驱动程序。 - Neal
显示剩余3条评论

83

17
当然,那不是真的,吝啬鬼(Stingy)。你需要筛选所有数据并编写糟糕的数据库代码(手工制作模型、将列映射到属性等),最快的方法是让其他人为你完成这些工作。这就是为什么人们使用框架而不是从头开始编写所有内容的原因。 - user1228
12
毫无价值的方法!在读取时将文本列截断为255个字符。小心!参见:https://dev59.com/-0nSa4cB1Zd3GeqPP6UP ACE引擎也会做同样的事情! - Triynko
5
请注意,使用 ADO.NET 从 Excel 中读取数据需要安装 Microsoft Access 或 Microsoft Access 数据库引擎可再发行程序。 - zihotki
3
司机还会根据前几行猜测列的类型。如果在前几行看起来像整数的一列中出现了非整数(例如浮点数、字符串),那么当你遇到这个错误时,你将会遇到一个错误。请注意,这是机器翻译,可能不够准确,仅供参考。 - Brian Low
1
如果你在一个64位进程中运行的话,这个方法完全不起作用。http://forums.asp.net/p/1128266/1781961.aspx - aquinas
显示剩余3条评论

27

ADO.NET方法快速简便,但有一些怪癖需要注意,尤其是涉及数据类型处理的方面。

这篇优秀文章将帮助您避免一些常见陷阱: http://blog.lab49.com/archives/196


你回答了我的问题(在上面的评论中)。 - Kevin Le - Khnle

22

这是我在 Excel 2003 中使用的内容:

Dictionary<string, string> props = new Dictionary<string, string>();
props["Provider"] = "Microsoft.Jet.OLEDB.4.0";
props["Data Source"] = repFile;
props["Extended Properties"] = "Excel 8.0";

StringBuilder sb = new StringBuilder();
foreach (KeyValuePair<string, string> prop in props)
{
    sb.Append(prop.Key);
    sb.Append('=');
    sb.Append(prop.Value);
    sb.Append(';');
}
string properties = sb.ToString();

using (OleDbConnection conn = new OleDbConnection(properties))
{
    conn.Open();
    DataSet ds = new DataSet();
    string columns = String.Join(",", columnNames.ToArray());
    using (OleDbDataAdapter da = new OleDbDataAdapter(
        "SELECT " + columns + " FROM [" + worksheet + "$]", conn))
    {
        DataTable dt = new DataTable(tableName);
        da.Fill(dt);
        ds.Tables.Add(dt);
    }
}

2
工作表未定义...在明确定义了其他所有内容之后,这似乎有点奇怪。 - Jeremy Holovacs

21

Excel Data Reader怎么样?

http://exceldatareader.codeplex.com/

我曾经在生产环境中使用过它,从不同的Excel文件中提取大量数据到SQL Server Compact中,效果很好而且非常稳定。


2
我也推荐使用Excel Data Reader;它还带来了非常有用的Excel数据驱动测试库,该库使用NUnit 2.5的TestCaseSource属性,使得使用Excel电子表格进行数据驱动测试变得非常容易。只需注意Resharper尚不支持TestCaseSource,因此您必须使用NUnit运行器。 - David Keaveny
很遗憾,我们刚刚遇到了一些与这个库有关的问题。首先,我们发现一些货币字段被错误地转换成了日期。其次,如果工作簿中有任何空白表格,它就会崩溃。因此,尽管它非常容易集成,但我们现在正在重新评估是否继续使用这个库。它似乎没有得到积极的开发。 - Ian1971
它还假定 xlsx 文件中存在一些可选元素,如果这些元素不存在,则会导致无法读取数据。 - RichieHindle
我们在处理来自SQL Server Reporting Services的Excel文件时遇到了问题。它们根本无法工作,除非您打开并保存它们(即使未编辑)。@RichieHindle:您所说的可选元素是什么(希望这可以帮助我处理我的SSRS Excel文件)? - Peter
@Peter:我认为在我的<worksheet>中缺少了一个<dimension>元素,这可能会给我带来麻烦。 - RichieHindle
作为对我上面评论的更新。我们确实继续使用了这个库,事实上,我和另一个人已经成为了该项目的开发人员,并且现在正在积极地进行工作。我提到的问题现在已经得到解决,开放办公室支持也得到了解决,希望SSRS也能得到解决(需要有人来测试)。 - Ian1971

16

以下是我几年前使用.NET 1.1写的一些C#代码。不确定这是否正好是你所需要的(也许不是我最好的代码 :))。

using System;
using System.Data;
using System.Data.OleDb;

namespace ExportExcelToAccess
{
    /// <summary>
    /// Summary description for ExcelHelper.
    /// </summary>
    public sealed class ExcelHelper
    {
        private const string CONNECTION_STRING = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=<FILENAME>;Extended Properties=\"Excel 8.0;HDR=Yes;\";";

        public static DataTable GetDataTableFromExcelFile(string fullFileName, ref string sheetName)
        {
            OleDbConnection objConnection = new OleDbConnection();
            objConnection = new OleDbConnection(CONNECTION_STRING.Replace("<FILENAME>", fullFileName));
            DataSet dsImport = new DataSet();

            try
            {
                objConnection.Open();

                DataTable dtSchema = objConnection.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);

                if( (null == dtSchema) || ( dtSchema.Rows.Count <= 0 ) )
                {
                    //raise exception if needed
                }

                if( (null != sheetName) && (0 != sheetName.Length))
                {
                    if( !CheckIfSheetNameExists(sheetName, dtSchema) )
                    {
                        //raise exception if needed
                    }
                }
                else
                {
                    //Reading the first sheet name from the Excel file.
                    sheetName = dtSchema.Rows[0]["TABLE_NAME"].ToString();
                }

                new OleDbDataAdapter("SELECT * FROM [" + sheetName + "]", objConnection ).Fill(dsImport);
            }
            catch (Exception)
            {
                //raise exception if needed
            }
            finally
            {
                // Clean up.
                if(objConnection != null)
                {
                    objConnection.Close();
                    objConnection.Dispose();
                }
            }


            return dsImport.Tables[0];
            #region Commented code for importing data from CSV file.
            //              string strConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;" +"Data Source=" + System.IO.Path.GetDirectoryName(fullFileName) +";" +"Extended Properties=\"Text;HDR=YES;FMT=Delimited\"";
            //
            //              System.Data.OleDb.OleDbConnection conText = new System.Data.OleDb.OleDbConnection(strConnectionString);
            //              new System.Data.OleDb.OleDbDataAdapter("SELECT * FROM " + System.IO.Path.GetFileName(fullFileName).Replace(".", "#"), conText).Fill(dsImport);
            //              return dsImport.Tables[0];

            #endregion
        }

        /// <summary>
        /// This method checks if the user entered sheetName exists in the Schema Table
        /// </summary>
        /// <param name="sheetName">Sheet name to be verified</param>
        /// <param name="dtSchema">schema table </param>
        private static bool CheckIfSheetNameExists(string sheetName, DataTable dtSchema)
        {
            foreach(DataRow dataRow in dtSchema.Rows)
            {
                if( sheetName == dataRow["TABLE_NAME"].ToString() )
                {
                    return true;
                }   
            }
            return false;
        }
    }
}

完全同意,Cherian。这段代码已经很多年了...甚至在我熟练使用Resharper之前就存在了 :) - hitec
2
代码很丑,但展示了如何获取表格名称,非常棒! - Sam

15

Koogra 是一个用C#编写的开源组件,可读写Excel文件。


与NPOI相比,它看起来不太活跃了。 - David Burton

12

8

我之前在C#中读取Excel文件的时候做了很多阅读,我们采用了两种方法:

  • COM API:您可以直接访问Excel的对象,并通过方法和属性进行操作。
  • ODBC驱动程序:允许您像使用数据库一样使用Excel。

后一种方法要快得多:通过COM读取具有20列和200行的大表需要30秒钟,而通过ODBC只需要半秒钟。因此,如果您只需要数据,则建议使用数据库方法。

谢谢,

Carl


6

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接