Время прочтения: 6 мин.

В процессе аудита часто возникает потребность в обработке данных из документов в форматах MS Word или Excel. В своей статье я хочу поделиться опытом считывания информации из таких файлов с использованием языка C#.

Для работы с файлами Word и Excel я решил выбрать библиотеки Microsoft.Office.Interop.Word и Microsoft.Office.Interop.Excel, предоставляющие программные интерфейсы для взаимодействия с объектами MS Word и Excel.

Преимущества использования этих библиотек:

  • созданы корпорацией Microsoft, следовательно, взаимодействие с объектами программ пакета MS Office реализовано наиболее оптимально,
  • нужный пакет Visual Studio Tool for Office поставляется вместе с Visual Studio (достаточно отметить его при установке VS).

Также следует заметить, что у такого похода есть и недостаток: для того, чтобы написанная программа работала на ПК пользователя необходимо, чтобы на нём были установлены программы MS Office и MS Excel. Поэтому такой подход плохо подходит для серверных решений. Также такая программа не будет являться кроссплатформенной.

Добавление библиотек в проект Visual Studio

Библиотеки поставляются вместе с пакетом Visual Studio Tool for Office (платформа .NET Framework).

Для использования библиотеки нужно:

  • добавить ссылку на неё: в обозревателе решений необходимо кликнуть правой кнопкой мыши по пункту Ссылки (Рис. 1) и найти нужную библиотеку по ключевым словам (после добавления ссылка появится в списке),
  • указать используемое пространство имён в файле программы (в примере ему назначен алиас Word): (Рис. 2):

Пример парсинга файла MS Word

Можно прочитать основные форматы: .doc, .docx, .rtf.

Ниже приведён листинг с примером считывания текста из документа MS Word:

object FileName = @"C:\test.doc";
object rOnly = true;
object SaveChanges = false;
object MissingObj = System.Reflection.Missing.Value;

Word.Application app = new Word.Application();
Word.Document doc = null;
Word.Range range = null;
try
{
    doc = app.Documents.Open(ref FileName, ref MissingObj, ref rOnly, ref MissingObj,
    ref MissingObj, ref MissingObj, ref MissingObj, ref MissingObj,
    ref MissingObj, ref MissingObj, ref MissingObj, ref MissingObj,
    ref MissingObj, ref MissingObj, ref MissingObj, ref MissingObj);

    object StartPosition = 0;
    object EndPositiojn = doc.Characters.Count;
    range = doc.Range(ref StartPosition, ref EndPositiojn);

    // Получение основного текста со страниц (без учёта сносок и колонтитулов)
    string MainText = (range == null || range.Text == null) ? null : range.Text;
    if (MainText != null)
    {
        /* Обработка основного текста документа*/
    }

    // Получение текста из нижних и верхних колонтитулов
    foreach (Word.Section section in doc.Sections)
    {
        // Нижние колонтитулы
        foreach (Word.HeaderFooter footer in section.Footers)
        {
            string FooterText = (footer.Range == null || footer.Range.Text == null) ? null : footer.Range.Text;
            if (FooterText != null)
            {
                /* Обработка текста */
            }
        }

        // Верхние колонтитулы
        foreach (Word.HeaderFooter header in section.Headers)
        {
            string HeaderText = (header.Range == null || header.Range.Text == null) ? null : header.Range.Text;
            if (HeaderText != null)
            {
                /* Обработка текста */
            }
        }
    }
    // Получение текста сносок
    if (doc.Footnotes.Count != 0)
    {
        foreach (Word.Footnote footnote in doc.Footnotes)
        {
            string FooteNoteText = (footnote.Range == null || footnote.Range.Text == null) ? null : footnote.Range.Text;
            if (FooteNoteText != null)
            {
                /* Обработка текста */
            }
        }
    }
} catch (Exception ex)
{
    /* Обработка исключений */
}
finally
{
    /* Очистка неуправляемых ресурсов */
    if(doc != null)
    {
        doc.Close(ref SaveChanges);
    }
    if(range != null)
    {
        Marshal.ReleaseComObject(range);
        range = null;
    }            
    if(app != null)
    {
        app.Quit();
        Marshal.ReleaseComObject(app);
        app = null;
    }
}

Примечания:

  • в коде приводится пример считывания основного текста документа, текста верхних и нижних колонтитулов, а также текста сносок,
  • в коде производится очистка неуправляемых ресурсов с использованием класса Marshal (подробнее можно почитать по ссылке )

Пример парсинга файла MS Excel

Можно прочитать основные форматы: .xls, .xlsx.

Ниже приведён листинг с примером считывания текста из документа MS Excel (по ячейкам):

string FileName = @"C:\Users\bee\Downloads\test.xlsx";
object rOnly = true;
object SaveChanges = false;
object MissingObj = System.Reflection.Missing.Value;

Excel.Application app = new Excel.Application();
Excel.Workbooks workbooks = null;
Excel.Workbook workbook = null;
Excel.Sheets sheets = null;
try
{
    workbooks = app.Workbooks;
    workbook  = workbooks.Open(FileName, MissingObj, rOnly, MissingObj, MissingObj,
                                MissingObj, MissingObj, MissingObj, MissingObj, MissingObj,
                                MissingObj, MissingObj, MissingObj, MissingObj, MissingObj);

    // Получение всех страниц докуента
    sheets = workbook.Sheets;
                
    foreach(Excel.Worksheet worksheet in sheets)
    {
        // Получаем диапазон используемых на странице ячеек
        Excel.Range UsedRange = worksheet.UsedRange;
        // Получаем строки в используемом диапазоне
        Excel.Range urRows = UsedRange.Rows;
        // Получаем столбцы в используемом диапазоне
        Excel.Range urColums = UsedRange.Columns;

        // Количества строк и столбцов
        int RowsCount = urRows.Count;
        int ColumnsCount = urColums.Count;
        for(int i = 1; i <= RowsCount; i++)
        {
            for(int j = 1; j <= ColumnsCount; j++)
            {
                Excel.Range CellRange = UsedRange.Cells[i, j];
                // Получение текста ячейки
                string CellText = (CellRange == null || CellRange.Value2 == null) ? null :
                                    (CellRange as Excel.Range).Value2.ToString();

                if(CellText != null)
                {
                    /* Обработка текста */
                }
            }
        }
        // Очистка неуправляемых ресурсов на каждой итерации
        if (urRows != null) Marshal.ReleaseComObject(urRows);
        if (urColums != null) Marshal.ReleaseComObject(urColums);
        if (UsedRange != null) Marshal.ReleaseComObject(UsedRange);
        if (worksheet != null) Marshal.ReleaseComObject(worksheet);
    }
} catch (Exception ex)
{
    /* Обработка исключений */
}
finally
{
    /* Очистка оставшихся неуправляемых ресурсов */
    if (sheets != null) Marshal.ReleaseComObject(sheets);
    if (workbook != null)
    {
        workbook.Close(SaveChanges);
        Marshal.ReleaseComObject(workbook);
        workbook = null;
    }

    if (workbooks != null)
    {
        workbooks.Close();
        Marshal.ReleaseComObject(workbooks);
        workbooks = null;
    }
    if (app != null)
    {
        app.Quit();
        Marshal.ReleaseComObject(app);
        app = null;
    }
}

Примечания:

  • при обработке текста каждой ячейки приходится заранее знать количество задействованных строк и столбцов на текущем листе документа,
  • такой перебор не совсем оптимален (временная сложность алгоритма O(n2)): при желании его можно ускорить (например, разбив обработку на несколько потоков): в данной статье приводится лишь пример получения текста из каждой ячейки,
  • при таком переборе ячеек необходимо на каждой итерации освобождать неуправляемые ресурсы, чтобы избежать утечек памяти (аналогично предыдущему примеру, используется класс Marshal).

Приведенные примеры хорошо подходят для реализации приложения по обработке документов Word и Excel на платформе .NET Framework.

С помощью указанных библиотек можно не только читать текст из документов, но и создавать новые файлы форматов MS Word и Excel.