Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Низкая производительность при чтении данных из листа Excel в ArrayList. C#

10K
27 октября 2011 года
Shalfey
47 / / 10.03.2007
Описание проблемы:

УСЛОВИЯ:

Нужно прочесть данные из Excel-файла и запихнуть их в ArrayList. В рабочей книги только один рабочий лист. Данные представляет собой вещественные числа. Все они расположены в одном столбце.

ПРОБЛЕМА:
Слишком много времени уходит на данный процесс - примерно 2 минуты чтоб наполнить ArrayList 10000 элементов.

Прошу вашей помощи в ускорении этого процесса. Ясно, что чтение данных в моей реализации неэффективно. Прошу вашего совета по повышению производительности. Желательно кусочек кода. Заранее спасибо.

Код:
// Method GetExcelData opens 1 excel file, reads data row by row and adds
    // it into the array of source Data Values (sourceDataValues in our case).
    private void GetExcelData(string fullPath, ArrayList arrForValues)
    {
        Excel.Application excelapp = new Excel.Application();
        excelapp.Visible = false;
        // to avoid appearing of Excel window on the screen
        Excel.Workbook excelappworkbook = excelapp.Workbooks.Open(
            fullPath,
            Type.Missing, Type.Missing, true, Type.Missing,
            Type.Missing, Type.Missing, Type.Missing, Type.Missing,
            Type.Missing, Type.Missing, Type.Missing, Type.Missing,
            Type.Missing, Type.Missing);
        Excel.Worksheet excelworksheet = (Excel.Worksheet)excelappworkbook.Worksheets.get_Item(1);
        Excel.Range excelcells = excelworksheet.UsedRange;
        uint rowsNum = 0;
        for (rowsNum = 1; rowsNum != excelcells.Rows.Count; rowsNum++)
        {
            arrForValues.Add((excelcells.Cells[rowsNum, 1] as Excel.Range).Value2);
        }
        excelappworkbook.Close(false, Type.Missing, Type.Missing);
        excelapp.Quit();
    }
341
28 октября 2011 года
Der Meister
874 / / 21.12.2007
Сгенерировал книгу с 10 000 значений типа double и попробовал поитерироваться вместо того, чтобы получать ячейки по индексу.
Код:
private static IList<double> ReadValues(string path)
{
    var application = new Application();
    try
    {
        var workbook = (Workbook)ComHelper.Invoke(application.Workbooks, "_Open", path);
        try
        {
            var sheet = (Worksheet)workbook.Worksheets[1];
            var range = sheet.UsedRange;

            var result = new List<double>(range.Count);
            foreach (Range value in range)
                result.Add((double) value.Value2);

            return result;
        }
        finally
        {
            ComHelper.Invoke(workbook, "Close");
        }
    }
    finally
    {
        application.Quit();
        ComHelper.Release(application);
    }
}

static class ComHelper
{
    public static object Invoke<T>(T obj, string method_name, params object[] args)
    {
        var type = typeof(T);

        const BindingFlags binding_flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly;
        var method = type.GetMethod(method_name, binding_flags);
        if (method == null)
        {
            method = obj.GetType().GetMethod(method_name, binding_flags);
            if (method == null)
                throw new ArgumentException(string.Format("Method {0} not found in type {1}", method_name, type.Name));
        }

        var parameters_count = method.GetParameters().Length;
        var provided_args_count = args.Length;
        if (provided_args_count > parameters_count)
        {
            var message = string.Format(
                "Too much parameters in {0}.{1}(). Need {2}, got {3}",
                type.Name,
                method_name,
                parameters_count,
                provided_args_count
            );

            throw new ArgumentException(message);
        }

        var all_args = new object[parameters_count];
        args.CopyTo(all_args, 0);
        for (var i = provided_args_count; i < parameters_count; i++)
            all_args = Missing.Value;

        try
        {
            return method.Invoke(obj, all_args);
        }
        catch (TargetInvocationException ex)
        {
            throw ex.InnerException;
        }
    }

    public static void Release(object com_object)
    {
        while (System.Runtime.InteropServices.Marshal.ReleaseComObject(com_object) != 0) { }
    }
}
Картина получилась интересная.
Время сократилось почти в три раза: 23 против 62 секунд, но это всё равно долго - уверен: можно быстрее. Замечательно здесь то, что само итерирование (foreach) - почти 40% этого времени. В общем, во вложении - лог (время в секундах) таймингов моего и вашего кода, посмотрите - мож чё и найдёте.
Можно ещё попробовать поставить ACE OLEDB 12 и выдернуть данные sql-запросом - сам не пробовал, но чем чёрт не шутит.
5
28 октября 2011 года
hardcase
4.5K / / 09.08.2005
Комментарий к коду. Зачем использовать рефлекскию, когда в C# 4 есть dynamic и/или передача аргументов по имени?
341
28 октября 2011 года
Der Meister
874 / / 21.12.2007
Это C# 3 и давнишняя наработка :) Оно всё аинхрен к тому и сведётся, в конечном итоге.
10K
29 октября 2011 года
Shalfey
47 / / 10.03.2007
Всё оказалось весьма просто. Быстрее всего сработало решение, предложенное здесь:
http://www.codeproject.com/Questions/274445/Low-performance-when-reading-data-from-Excel-workb
Вот конечный вариант:
Код:
private void GetExcelData(string fullPath, List<double> arrForValues)
        {
            Excel.Application excelapp = new Excel.Application();
            excelapp.Visible = false;
            // to avoid appearing of Excel window on the screen
            Excel.Workbook excelappworkbook = excelapp.Workbooks.Open(
                fullPath,
                Type.Missing, Type.Missing, true, Type.Missing,
                Type.Missing, Type.Missing, Type.Missing, Type.Missing,
                Type.Missing, Type.Missing, Type.Missing, Type.Missing,
                Type.Missing, Type.Missing);
            Excel.Worksheet excelworksheet = (Excel.Worksheet)excelappworkbook.Worksheets.get_Item(1);
            Excel.Range excelcells = excelworksheet.UsedRange;
            object[,] worksheetValuesArray = excelcells.get_Value(Type.Missing);
           
            for (int col = 1; col < (worksheetValuesArray.GetLength(1)+1); col++)
            {
                for (int row = 1; row < (worksheetValuesArray.GetLength(0)+1); row++)
                {
                    arrForValues.Add((double) worksheetValuesArray[row, col]);
                }
            }
            excelappworkbook.Close(false, Type.Missing, Type.Missing);
            excelapp.Quit();
        }
297
29 октября 2011 года
koodeer
1.2K / / 02.05.2009
Позвольте я ещё дам совет: Never use 2 dots with COM objects!
http://stackoverflow.com/questions/158706/how-to-properly-clean-up-excel-interop-objects-in-c-sharp
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог