Показать сообщение отдельно
Старый 21.05.2010, 16:58   #14  
Gustav is offline
Gustav
Moderator
Аватар для Gustav
SAP
Лучший по профессии 2009
 
1,858 / 1152 (42) ++++++++
Регистрация: 24.01.2006
Адрес: Санкт-Петербург
Записей в блоге: 19
Что ж, "утром - в газете, вечером - в куплете". Сваял я демонстрашку по этой задачке. Время сравнения двух множеств из заявленных 4500 элементов и перебора их расхождений у меня сплошь и рядом менее 1 секунды!

Перед запуском джоба необходимо зарегистрировать на компе упомянутую выше dll-ку AndyD'а (Построчный импорт из Excel через COM). После загрузки формы измените какие-нибудь ячейки (сотрите или замените иным текстом в ячейке). Закройте форму крестиком - в инфологе список расхождений. Имейте в виду, что время вывода в инфолог - "огромно" по сравнению с временем сравнения и перебора, поэтому оно и вынесено в отдельный цикл за пределы засечки по времени.
X++:
static void job_SpreadsheetAsTableProcessor(Args _args)
{
    Form                form = new Form();
    FormBuildDesign     formBuildDesign = form.addDesign('Design');
    Args                args = new Args();
    FormRun             formRun;
    FormActiveXControl  ss;

    COM                 SafeArray = new COM('AxSafeArray.SafeArray');
    COM                 range;
    COMVariant          cvRange;
    COMVariant          cvRow, cvCell;
    Array               arr;

    int                 rowCount, colCount;
    int                 row, col;
    str                 val;

    Set                 setBefore = new Set(Types::Container);
    Set                 setAfter  = new Set(Types::Container);
    Set                 setDiffer = new Set(Types::Container);
    SetEnumerator       enumr;

    int                 timeStart, timeDuration;
    ;

    // динамически генерируем форму
    args.object(form);

    formRun = classFactory.formRunClass(args);
    formRun.init();
    formRun.design().caption('Табличный процессор');

    ss = formRun.design().addControl(FormControlType::ActiveX, 'Spreadsheet');
    ss.className('{0002E541-0000-0000-C000-000000000046}');     // Microsoft Office Spreadsheet 10.0
    //ss.className('{0002E559-0000-0000-C000-000000000046}');   // Microsoft Office Spreadsheet 11.0

    ss.heightMode(FormHeight::ColumnHeight);
    ss.widthMode(FormWidth::ColumnWidth);

    range = ss.Range('A1:O300'); // 15 колонок - 300 строк
    range.Formula('="["&ROW()&","&COLUMN()&"]"'); // или, если будет ругаться, то: range.Formula('="["&СТРОКА()&","&СТОЛБЕЦ()&"]"');

    cvRange = range.Value2();

    rowCount = SafeArray.GetArrayRowCount(cvRange);
    colCount = SafeArray.GetArrayColCount(cvRange);

    // записываем состояние ячеек ДО изменения
    for (row=1; row<=rowCount; row++)
    {
        cvRow = SafeArray.GetArrayRow(cvRange, row);
        arr = cvRow.safeArray();
        for (col=1; col<=colCount; col++)
        {
            cvCell = arr.value(col);
            setBefore.add([row, col, cvCell.bStr()]);
        }
    }

    formRun.run();
    formRun.wait(); // здесь вручную изменяем несколько ячеек и закрываем форму крестиком

    timeStart = WinAPI::getTickCount();

    // записываем состояние ячеек ПОСЛЕ изменения
    cvRange = range.Value2();

    for (row=1; row<=rowCount; row++)
    {
        cvRow = SafeArray.GetArrayRow(cvRange, row);
        arr = cvRow.safeArray();
        for (col=1; col<=colCount; col++)
        {
            cvCell = arr.value(col);
            setAfter.add([row, col, cvCell.bStr()]);
        }
    }

    // находим расхождения в состояниях
    setDiffer = Set::difference( setAfter, setBefore );

    // "трогаем" все значения расхождений - как бы используем для каких-то наших целей
    enumr = setDiffer.getEnumerator();
    while (enumr.moveNext())
    {
        [row,col,val] = enumr.current();
    }

    timeDuration = WinAPI::getTickCount() - timeStart;
    info(strFmt('Сравнение и перебор расхождений выполнены за %1 миллисек.', timeDuration));

    setPrefix('Изменились следующие ячейки:');
    // отдельным циклом, чтобы исключить время вывода в инфолог
    enumr = setDiffer.getEnumerator();
    while (enumr.moveNext())
    {
        [row,col,val] = enumr.current();
        info(strFmt('Строка: %1 -- Столбец: %2 -- Новое значение: %3', row, col, val));
    }
}
Кстати, приложение "Переводчик" на сайте "ВКонтакте" переводит словосочетание "Табличный процессор" с русского на английский как "Spreadsheet". Я порадовался

P.S. А если фрагмент вывода в инфолог слегка изменить, то удобнее (по крайней мере, с точки зрения тестирования) будет вывести адрес ячейки вместо номеров строки и столбца:
X++:
    COM                 cells, cell;  // добавлено
    ...........
    setPrefix('Изменились следующие ячейки:');
    // отдельным циклом, чтобы исключить время вывода в инфолог
    cells = ss.Cells(); // добавлено
    enumr = setDiffer.getEnumerator();
    while (enumr.moveNext())
    {
        [row,col,val] = enumr.current();
        cell = cells.Item(row,col); // добавлено
        info(strFmt('Адрес: %1 -- Новое значение: %2', cell.Address(false,false), val)); // изменено
    }

Последний раз редактировалось Gustav; 21.05.2010 в 19:33.
За это сообщение автора поблагодарили: Serg (1), Poleax (1).