Что ж, "утром - в газете, вечером - в куплете". Сваял я демонстрашку по этой задачке. Время сравнения двух множеств из заявленных 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)); // изменено
}