读取Excel文件(.xls / .xlsx)的最佳方法


76

我知道有多种读取Excel文件的方法:

  • Iterop
  • Oledb
  • Open Xml SDK

兼容性不是问题,因为程序将在受控环境中执行。

我的要求:
将文件读为DataTable/ CUstom Entities(我不知道如何为对象创建动态属性/字段[列名在Excel文件中会有所不同])

用于DataTable/Custom Entities使用其数据执行某些操作。

更新DataTable操作结果

写回excel file

这会更简单。

另外,如果可能的话,请教我有关自定义实体的信息(将属性/字段动态添加到对象)


@AmiramKorach将什么内容写回到excel ....
Ankesh 2012年

为此,我使用了商业第三方。这已经在这里问stackoverflow.com/questions/1527790/...
阿米拉姆授予Korach

我认为,最有效的方法之一是用具有直接的方法GemBox.Spreadsheet库出口DataTable出口DataTable
Hazel Patton

只是一些有用的建议,excel文件只是zip文件。提取一个excel文件将为您提供几个文件夹。文件的字符串存储在“ [filenamefolder] /xl/sharedStrings.xml”中,工作簿存储在“ [filenamefolder] /xl/workbook.xml”中。理论上,您可以通过编程方式解压缩excel文件并从提取的文件。
Zach Pedigo

Answers:


72

看一看Linq-to-Excel。非常干净。

var book = new LinqToExcel.ExcelQueryFactory(@"File.xlsx");

var query =
    from row in book.Worksheet("Stock Entry")
    let item = new
    {
        Code = row["Code"].Cast<string>(),
        Supplier = row["Supplier"].Cast<string>(),
        Ref = row["Ref"].Cast<string>(),
    }
    where item.Supplier == "Walmart"
    select item;

它还允许强类型的行访问。


4
请注意,Linq-to-Excel使用第三方库列表。
fschricker 2014年

5
@fschricker-只有两个-“ log4net”和“ Remotion”。
谜团2014年

11
和Access Database Engine,尽管Microsoft是另一个依赖项。
艾伦B

4
不错...由于“访问数据库引擎”,这不是可行的生产资产。
囚犯零'18

8
不是成为“巨魔” ...而是...以防万一有人在考虑使用此功能。您“可能”使用它的唯一位置是在您自己的桌面上。没有人会允许您在客户端桌面或Web服务器上安装“访问数据库引擎” ...而且(出于充分的理由)很多地方甚至不允许您在本地安装类似的东西。同样,我喜欢语法,也喜欢这个主意...但这并不是一个广泛可行的解决方案。仍然...非常酷。
囚犯零'18

27

使用OLE查询,这非常简单(例如sheetName为Sheet1):

DataTable LoadWorksheetInDataTable(string fileName, string sheetName)
{           
    DataTable sheetData = new DataTable();
    using (OleDbConnection conn = this.returnConnection(fileName))
    {
       conn.Open();
       // retrieve the data using data adapter
       OleDbDataAdapter sheetAdapter = new OleDbDataAdapter("select * from [" + sheetName + "$]", conn);
       sheetAdapter.Fill(sheetData);
       conn.Close();
    }                        
    return sheetData;
}

private OleDbConnection returnConnection(string fileName)
{
    return new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + fileName + "; Jet OLEDB:Engine Type=5;Extended Properties=\"Excel 8.0;\"");
}

对于较新的Excel版本:

return new OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + fileName + ";Extended Properties=Excel 12.0;");

您还可以在CodePlex上使用Excel Data Reader一个开源项目。从Excel工作表中导出数据确实效果很好。

在指定的链接上给出的示例代码:

FileStream stream = File.Open(filePath, FileMode.Open, FileAccess.Read);

//1. Reading from a binary Excel file ('97-2003 format; *.xls)
IExcelDataReader excelReader = ExcelReaderFactory.CreateBinaryReader(stream);
//...
//2. Reading from a OpenXml Excel file (2007 format; *.xlsx)
IExcelDataReader excelReader = ExcelReaderFactory.CreateOpenXmlReader(stream);
//...
//3. DataSet - The result of each spreadsheet will be created in the result.Tables
DataSet result = excelReader.AsDataSet();
//...
//4. DataSet - Create column names from first row
excelReader.IsFirstRowAsColumnNames = true;
DataSet result = excelReader.AsDataSet();

//5. Data Reader methods
while (excelReader.Read())
{
//excelReader.GetInt32(0);
}

//6. Free resources (IExcelDataReader is IDisposable)
excelReader.Close();

参考:如何使用Microsoft.Office.Interop.Excel从Excel导入数据集?


4
代码在this.returnConnection(fileName)哪里?
马斯洛

这在.NET CoreOleDb中不再可用,因为它不可用(因为它不是跨平台的)。
codeMonkey

ExcelDataReader为我工作,但现在托管在github.com/ExcelDataReader/ExcelDataReader
Wernsey

1
是否每个选项都要求我安装Access数据库引擎?
Jamshaid Kamran

System.Data.OleDb现在可用于dotnet核心nuget.org/packages/System.Data.OleDb
nedstark179

22

我意识到这个问题是在将近7年前提出的,但是对于某些有关使用C#导入excel数据的关键字,它仍然是Google排名最高的搜索结果,因此我想根据最近的一些技术发展提供一个替代方法。

导入Excel数据已成为我日常工作中的一项常见任务,因此我简化了流程并在博客上记录了该方法:在c#中读取excel文件的最佳方法

我使用NPOI是因为它可以在未安装Microsoft Office的情况下读取/写入Excel文件,并且不使用COM +或任何互操作性。这意味着它可以在云中工作!

但是真正的魔力来自与Donny Tian的NPOI Mapper的配对,因为它使我无需编写任何代码即可将Excel列映射到C#类中的属性。真漂亮。

这是基本思想:

我创建一个.net类来匹配/映射我感兴趣的Excel列:

        class CustomExcelFormat
        {
            [Column("District")]
            public int District { get; set; }

            [Column("DM")]
            public string FullName { get; set; }

            [Column("Email Address")]
            public string EmailAddress { get; set; }

            [Column("Username")]
            public string Username { get; set; }

            public string FirstName
            {
                get
                {
                    return Username.Split('.')[0];
                }
            }

            public string LastName
            {
                get
                {
                    return Username.Split('.')[1];
                }
            }
        }

注意,如果需要的话,它允许我基于列名进行映射!

然后,当我处理excel文件时,我需要做的就是这样:

        public void Execute(string localPath, int sheetIndex)
        {
            IWorkbook workbook;
            using (FileStream file = new FileStream(localPath, FileMode.Open, FileAccess.Read))
            {
                workbook = WorkbookFactory.Create(file);
            }

            var importer = new Mapper(workbook);
            var items = importer.Take<CustomExcelFormat>(sheetIndex);
            foreach(var item in items)
            {
                var row = item.Value;
                if (string.IsNullOrEmpty(row.EmailAddress))
                    continue;

                UpdateUser(row);
            }

            DataContext.SaveChanges();
        }

现在,坦率地说,我的代码不会修改Excel文件本身。我改为使用Entity Framework将数据保存到数据库中(这就是为什么在我的示例中看到“ UpdateUser”和“ SaveChanges”的原因)。但是,关于SO如何使用NPOI保存/修改文件已经有很好的讨论。


1
这就像一个魅力!到目前为止最简单的解决方案。两者都可以作为NuGet包使用。
斯特凡

1
您好Dan,如果没有服务器中的excel软件或oledb驱动程序,我将无法解决excel文件处理问题。但是使用这个,我得以实现。性能也不错。非常感谢。
Ramakrishnankt

1
如何在不知道列名的情况下将其用于一般excel文件?例如,我希望将工作表导出到DataTable中。
David Piao

David Pio-使用新dynamic功能。我想这就是您要寻找的。mapper.Take<dynamic>(0).ToList();
Piotr Kula

1
很棒,像魅力一样工作,节省了大量时间。谢谢!
Flatpick13

6

尝试使用这种免费方式,https://freenetexcel.codeplex.com

 Workbook workbook = new Workbook();

 workbook.LoadFromFile(@"..\..\parts.xls",ExcelVersion.Version97to2003);
 //Initialize worksheet
 Worksheet sheet = workbook.Worksheets[0];

 DataTable dataTable = sheet.ExportDataTable();

5

如果您只能将它限制为(Open Office XML格式)*。xlsx文件,那么最受欢迎的库可能就是EPPLus

值得庆幸的是,没有其他依赖项。只需使用nuget进行安装:

Install-Package EPPlus

0

尝试使用Aspose.cells库(不是免费的,但是可以尝试使用),这是非常好的

Install-package Aspose.cells

有示例代码:

using Aspose.Cells;
using System;

namespace ExcelReader
{
    class Program
    {
        static void Main(string[] args)
        {
            // Replace path for your file
            readXLS(@"C:\MyExcelFile.xls"); // or "*.xlsx"
            Console.ReadKey();
        }

        public static void readXLS(string PathToMyExcel)
        {
            //Open your template file.
            Workbook wb = new Workbook(PathToMyExcel);

            //Get the first worksheet.
            Worksheet worksheet = wb.Worksheets[0];

            //Get cells
            Cells cells = worksheet.Cells;

            // Get row and column count
            int rowCount = cells.MaxDataRow;
            int columnCount = cells.MaxDataColumn;

            // Current cell value
            string strCell = "";

            Console.WriteLine(String.Format("rowCount={0}, columnCount={1}", rowCount, columnCount));

            for (int row = 0; row <= rowCount; row++) // Numeration starts from 0 to MaxDataRow
            {
                for (int column = 0; column <= columnCount; column++)  // Numeration starts from 0 to MaxDataColumn
                {
                    strCell = "";
                    strCell = Convert.ToString(cells[row, column].Value);
                    if (String.IsNullOrEmpty(strCell))
                    {
                        continue;
                    }
                    else
                    {
                        // Do your staff here
                        Console.WriteLine(strCell);
                    }
                }
            }
        }
    }
}

1
它不是免费的,并且在完成一些解析后(大约1K,我有许可证限制)
Mehmet Kurtipek

-1

从excel读取,修改并写回

 /// <summary>
/// /Reads an excel file and converts it into dataset with each sheet as each table of the dataset
/// </summary>
/// <param name="filename"></param>
/// <param name="headers">If set to true the first row will be considered as headers</param>
/// <returns></returns>
public DataSet Import(string filename, bool headers = true)
{
    var _xl = new Excel.Application();
    var wb = _xl.Workbooks.Open(filename);
    var sheets = wb.Sheets;
    DataSet dataSet = null;
    if (sheets != null && sheets.Count != 0)
    {
        dataSet = new DataSet();
        foreach (var item in sheets)
        {
            var sheet = (Excel.Worksheet)item;
            DataTable dt = null;
            if (sheet != null)
            {
                dt = new DataTable();
                var ColumnCount = ((Excel.Range)sheet.UsedRange.Rows[1, Type.Missing]).Columns.Count;
                var rowCount = ((Excel.Range)sheet.UsedRange.Columns[1, Type.Missing]).Rows.Count;

                for (int j = 0; j < ColumnCount; j++)
                {
                    var cell = (Excel.Range)sheet.Cells[1, j + 1];
                    var column = new DataColumn(headers ? cell.Value : string.Empty);
                    dt.Columns.Add(column);
                }

                for (int i = 0; i < rowCount; i++)
                {
                    var r = dt.NewRow();
                    for (int j = 0; j < ColumnCount; j++)
                    {
                        var cell = (Excel.Range)sheet.Cells[i + 1 + (headers ? 1 : 0), j + 1];
                        r[j] = cell.Value;
                    }
                    dt.Rows.Add(r);
                }

            }
            dataSet.Tables.Add(dt);
        }
    }
    _xl.Quit();
    return dataSet;
}



 public string Export(DataTable dt, bool headers = false)
    {
        var wb = _xl.Workbooks.Add();
        var sheet = (Excel.Worksheet)wb.ActiveSheet;
        //process columns
        for (int i = 0; i < dt.Columns.Count; i++)
        {
            var col = dt.Columns[i];
            //added columns to the top of sheet
            var currentCell = (Excel.Range)sheet.Cells[1, i + 1];
            currentCell.Value = col.ToString();
            currentCell.Font.Bold = true;
            //process rows
            for (int j = 0; j < dt.Rows.Count; j++)
            {
                var row = dt.Rows[j];
                //added rows to sheet
                var cell = (Excel.Range)sheet.Cells[j + 1 + 1, i + 1];
                cell.Value = row[i];
            }
            currentCell.EntireColumn.AutoFit();
        }
        var fileName="{somepath/somefile.xlsx}";
        wb.SaveCopyAs(fileName);
        _xl.Quit();
        return fileName;
    }

请以最低工作密码回答!
圣雷莫

您要解决的问题是什么?
贝宁
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.