Оптимальный способ чтения файла Excel (.xls/.xlsx)

Я знаю, что есть разные способы чтения файла Excel:

  • Iterop
  • Oledb
  • Open Xml SDK

Совместимость не является проблемой, поскольку программа будет выполняться в контролируемой среде.

Мое требование :
прочитать файл в DataTable / CUstom Entities (я не знаю, как сделать динамические свойства/поля для объекта [имена столбцов будут варьироваться в файле Excel])

Используйте DataTable/Custom Entities для выполнения некоторых операций с его данными.

Обновление DataTable с результатами операций

Напишите ответ excel file.

Что было бы проще.

Также, если возможно, посоветуйте мне пользовательские сущности (динамическое добавление свойств/полей к объекту)


person Ankesh    schedule 21.10.2012    source источник
comment
@AmiramKorach, что за ответ, чтобы преуспеть ....   -  person Ankesh    schedule 21.10.2012
comment
Я использую коммерческую третью сторону для этого. Это было задано здесь stackoverflow. ком/вопросы/1527790/   -  person Amiram Korach    schedule 22.10.2012
comment
Я думаю, что одним из наиболее эффективных способов является использование библиотеки GemBox.Spreadsheet, которая имеет прямые методы для экспортировать DataTable на лист и экспорт листа в DataTable.   -  person Hazel Patton    schedule 24.12.2019
comment
Просто небольшой полезный совет, файлы Excel - это просто zip-файлы. После извлечения файла Excel у вас останется несколько папок. Строки для файла хранятся в [filenamefolder]/xl/sharedStrings.xml, а рабочие книги хранятся в [filenamefolder]/xl/workbook.xml. Теоретически вы можете просто программно разархивировать файл excel и извлечь информацию из извлеченных файлов.   -  person Zach Pedigo    schedule 04.08.2020


Ответы (7)


Взгляните на 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;

Это также позволяет осуществлять строго типизированный доступ к строкам.

person Enigmativity    schedule 21.10.2012
comment
Обратите внимание, что Linq-to-Excel использует список сторонних библиотек. - person fschricker; 28.07.2014
comment
@fschricker - Только два - log4net и Remotion. - person Enigmativity; 29.07.2014
comment
Классная библиотека. Спасибо, что сообщили нам об этом. - person shahar eldad; 01.08.2014
comment
И Access Database Engine, который, хотя и Microsoft, является еще одной зависимостью. - person Alan B; 10.03.2015
comment
@CodeIt - Да, это так. - person Enigmativity; 21.09.2017
comment
Хотя приятно... это не является жизнеспособным производственным активом из-за ядра базы данных Access. - person Prisoner ZERO; 12.07.2018
comment
@PrisonerZERO - Да, но необходимость чтения файла Excel уже ставит его в нежизнеспособное пространство производственных активов. - person Enigmativity; 13.07.2018
comment
Не для того, чтобы быть "троллем"... но... на тот случай, если кто-то думает об использовании этого. Единственное место, где вы, вероятно, можете использовать его, — это ваш собственный рабочий стол. Никто не позволит вам установить Access Database Engine на клиентские рабочие столы или веб-серверы... и во многих местах (по уважительной причине) вам даже не позволят установить подобные вещи локально. Опять же, мне нравится синтаксис, и мне нравится идея... но это не очень жизнеспособное решение. Тем не менее... очень круто. - person Prisoner ZERO; 13.07.2018
comment
@PrisonerZERO Рискуя задать глупый вопрос, что особенно оскорбительного в зависимости от Access Database Engine? Вы, кажется, непреклонны в том, что это проблематично, но я хотел бы знать, почему, если не возражаете объяснить. - person Adam Davis; 16.10.2018
comment
@kevinc - Это правда. Но почему вы упоминаете об этом? ОП не спрашивал о переносимости. - person Enigmativity; 30.12.2018
comment
@Enigmativity правда? потому что он не будет работать на подавляющем большинстве компьютеров в мире. - person kevinc; 01.01.2019
comment
@kevinc - Это неправда. Вам требуется ПК с Windows (который есть в большинстве настольных операционных систем). - person Enigmativity; 02.01.2019
comment
Да, я согласен с @Enigmativity - переносимость не похожа на проблему, которую обсуждал Prisoner Zero. Очевидно, что это не вариант, если вы работаете в среде Linux, но в среде Windows эта зависимость не кажется особенно хуже, чем любая другая зависимость. - person Adam Davis; 29.01.2019

Используя OLE Query, это довольно просто (например, имя листа — Лист1):

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;");

Вы также можете использовать Excel Data Reader проект с открытым исходным кодом на CodePlex. Он очень хорошо работает для экспорта данных из листов 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();

Ссылка: Как импортировать из Excel в Набор данных с использованием Microsoft.Office.Interop.Excel?

person Furqan Safdar    schedule 21.10.2012
comment
где код для this.returnConnection(fileName)? - person Maslow; 13.08.2014
comment
Это больше не работает в .NET Core, так как OleDb недоступен (поскольку он не кроссплатформенный). - person codeMonkey; 04.12.2018
comment
У меня работал ExcelDataReader, но теперь он размещен на github.com/ExcelDataReader/ExcelDataReader. - person Wernsey; 16.01.2020
comment
Потребуется ли для каждого варианта установка ядра базы данных Access? - person Jamshaid K.; 20.02.2020
comment
System.Data.OleDb теперь доступен для ядра dotnet nuget.org/packages/System.Data .ОлеДб - person nedstark179; 16.08.2020

Я понимаю, что этот вопрос был задан почти 7 лет назад, но он по-прежнему является одним из лучших результатов поиска Google по определенным ключевым словам, касающимся импорта данных Excel с помощью С#, поэтому я хотел предоставить альтернативу, основанную на некоторых последних технических разработках.

Импорт данных из Excel стал настолько распространенной задачей среди моих повседневных обязанностей, что я упростил этот процесс и задокументировал метод в своем блоге: лучший способ чтения файла excel в c#.

Я использую NPOI, потому что он может читать/записывать файлы Excel без установленного Microsoft Office и не используйте COM+ или любое взаимодействие. Это означает, что он может работать в облаке!

Но настоящее волшебство происходит в паре с 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 уже есть хорошая дискуссия о том, как сохранить/изменить файл с помощью НКО.

person Dan    schedule 18.02.2019
comment
Это работает как шарм! Самое простое решение на данный момент. Оба доступны в виде пакета NuGet. - person Stefan; 25.05.2019
comment
Привет, Дэн, я не смог разрешить обработку файлов Excel без программного обеспечения Excel или драйверов oledb на сервере. Но используя это, я смог добиться. Также хорошая производительность. Большое спасибо. - person Ramakrishnankt; 24.12.2019
comment
Как я могу использовать это для общих файлов Excel, не зная их имен столбцов раньше? Например, я бы предпочел экспортировать лист в DataTable. - person David Piao; 11.01.2020
comment
Дэвид Пио — используйте новую функцию dynamic. Я думаю, это то, что вы ищете. mapper.Take<dynamic>(0).ToList(); - person Piotr Kula; 01.07.2020
comment
Потрясающе, работал как шарм и сэкономил кучу времени. Спасибо! - person Flatpick13; 20.07.2020
comment
Не могли бы вы поделиться проблеском фактического файла Excel, который у вас есть для этого? Кроме того, работает ли это с ядром .net? - person shaikhspear; 15.01.2021

Попробуйте использовать этот бесплатный способ, 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();
person Hark.Tenl    schedule 24.04.2014

Если вы можете ограничить ее только файлами *.xlsx (формат Open Office XML), то, вероятно, самой популярной библиотекой будет EPPLus .

Бонус в том, что других зависимостей нет. Просто установите с помощью nuget:

Install-Package EPPlus
person davewasthere    schedule 19.10.2017

Попробуйте воспользоваться библиотекой 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);
                    }
                }
            }
        }
    }
}
person fr0ga    schedule 24.05.2019
comment
это не бесплатно и имеет лицензионные ограничения после того, как будет выполнен некоторый синтаксический анализ (я думаю, 1K или около того) - person Mehmet Kurtipek; 26.11.2019

Чтение из 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;
    }
person Beingnin    schedule 24.01.2020
comment
Что такое _xl.Workbooks? - person javdromero; 23.04.2021