Чтение больших файлов Excel с помощью c # и получение индексов

Я пытался использовать Microsoft.Office.Interop.Excel, но он слишком медленно читал большие документы Excel (у меня это занимало более 5 минут). Я читал, что DocumentFormat.OpenXml быстрее, когда дело доходит до чтения больших документов Excel, но в документация, похоже, я не могу хранить столбцы и индексы строк.

На данный момент меня также интересует только первая строка, чтобы получить заголовки столбцов, и я буду читать остальную часть документа после некоторой логики. Мне не удалось найти способ прочитать только часть документа Excel. Я хочу сделать что-то подобное:

        int r = 1;  //row index
        int c = 1;  //column index
        while (xlRange.Cells[r,c] != null && xlRange.Cells[r, c].Value2 != null)
        {
            TagListData.Add(new TagClass { IsTagSelected = false, TagName = xlRange[r, c].Value2.toString(), rIndex = r, cIndex = c });
            c += 3;
        }

Пользователи будут выбирать документы Excel через openFileDialog, поэтому я не могу использовать фиксированное количество строк столбцов. Есть ли способ заставить это работать?

Спасибо


person Jummi    schedule 07.08.2018    source источник
comment
Загляните в Accor.IO.ExcelReader, может это поможет, а может и нет   -  person G. LC    schedule 08.08.2018


Ответы (1)


В OpenXML, если в ячейке нет текста, она может отображаться или не отображаться в списке ячеек (зависит от того, был ли в ней текст или нет). Поэтому подход типа while (...Value2 != null) на самом деле не является безопасным способом делать что-то в OpenXML.

Вот очень простой подход к чтению первой строки (написанной с использованием LINQPad, отсюда Main и Dump). Обратите внимание на (упрощенное) использование SharedStringTable для получения реального текста ячейки:

void Main()
{
    var fileName = @"c:\temp\openxml-read-row.xlsx";

    using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
    {
        using (SpreadsheetDocument doc = SpreadsheetDocument.Open(fs, false))
        {

            // Get the necessary bits of the doc
            WorkbookPart workbookPart = doc.WorkbookPart;
            SharedStringTablePart sstpart = workbookPart.GetPartsOfType<SharedStringTablePart>().First();
            SharedStringTable sst = sstpart.SharedStringTable;
            WorkbookStylesPart ssp = workbookPart.GetPartsOfType<WorkbookStylesPart>().First();
            Stylesheet ss = ssp.Stylesheet;

            // Get the first worksheet
            WorksheetPart worksheetPart = workbookPart.WorksheetParts.First();
            Worksheet sheet = worksheetPart.Worksheet;

            var rows = sheet.Descendants<Row>();
            var row = rows.First();
            foreach (var cell in row.Descendants<Cell>())
            {
                var txt = GetCellText(cell, sst);
                // LINQPad specific method .Dump()
                $"{cell.CellReference} = {txt}".Dump();
            }
        }
    }   
}

// Very basic way to get the text of a cell
private string GetCellText(Cell cell, SharedStringTable sst)
{
    if (cell == null)
        return "";

    if ((cell.DataType != null) && (cell.DataType == CellValues.SharedString))
    {
        int ssid = int.Parse(cell.CellValue.Text);
        string str = sst.ChildElements[ssid].InnerText;
        return str;
    }
    else if (cell.CellValue != null)
    {
        return cell.CellValue.Text;
    }
    return "";
}

Однако ... с OpenXML может потребоваться много работы, и вам следует попробовать использовать что-то вроде ClosedXML или EPPlus вместо этого.

например, используя ClosedXML

using (var workbook = new XLWorkbook(fileName))
{
    var worksheet = workbook.Worksheets.First();
    var row = worksheet.Row(1);
    foreach (var cell in row.CellsUsed())
    {
        var txt = cell.Value.ToString();
        // LINQPad specific method .Dump()
        $"{cell.Address.ToString()} = {txt}".Dump();
    }
}
person shunty    schedule 08.08.2018
comment
Спасибо за ответ, но, похоже, он не работает для моего файла Excel. Кажется, это работает для небольших документов Excel (я пробовал это с документом только с одной ячейкой), но, похоже, он использует слишком много памяти для моего большего файла Excel. - person Jummi; 08.08.2018
comment
Я обнаружил, что создание экземпляра книги замедляет мою программу. В моем коде есть: string filename = openFileDialog.FileName; using (var workbook = new XLWorkbook (filename)) {// здесь все закомментировано} Только это вызывает ошибку. Что еще я могу сделать? - person Jummi; 09.08.2018
comment
Похоже, у ClosedXML есть (были?) Проблемы с большими файлами. См. Здесь: github.com/ClosedXML/ClosedXML/issues/86. Кажется, есть бета-версия, которая может это исправить. Возможно, вам придется придерживаться подхода OpenXML и файлового потока. Или отключите события, упомянутые в приведенной выше проблеме: github.com/ClosedXML/ClosedXML/ wiki / Turnning-off-events. У меня никогда не было этих проблем, поэтому, боюсь, я ничего не понимаю. - person shunty; 09.08.2018
comment
Пришлось отключить ContextSwitchDeadLock в меню отладки. ClosedXML сейчас работает, но чтение файла по-прежнему занимает ›8 минут. Сейчас я использую ExcelDataReader, и мне потребовалось 4-5 минут, чтобы прочитать файл, но я постараюсь найти способ сделать это быстрее, чем это. Однако, спасибо - person Jummi; 10.08.2018