HotXLS сравнява две работни книги чрез TXLSXWorkbookCompare, който съпоставя работни листове по име, обхожда попълнените клетки на всяка двойка и отчита разликите като структуриран списък от записи за разлики и, при поискване, като по един четим ред за всяка разлика. Не е нужна инсталация на Excel, а сравнението се извършва изцяло върху заредения обектен модел в Delphi или C++Builder
Нуждата обикновено се появява първия път, когато някой попита какво се е променило. Финансова работна книга се връща от преглед, нощен експорт се регенерира след промяна в кода, или два отдела изпращат версии на един и същ шаблон. Отварянето на двете една до друга работи за един лист и се проваля за двадесет. Сравняването на файлове байт по байт не отговаря на нищо, защото два записа на една и съща работна книга се различават по начини, за които никой не се интересува
Какво се брои за разлика?
Сравнението отчита осем вида, и наборът е съзнателно малък: добавен или премахнат лист, добавена или премахната попълнена клетка, клетка, чиято стойност се е променила, клетка, чиято формула се е променила, и добавена или премахната обединена област. Всичко се изразява спрямо лявата работна книга като база, така че добавен елемент съществува само от дясната страна, а премахнат — само от лявата
Листовете се съпоставят по име, а не по позиция. Преподреждането на работни листове следователно не произвежда никакви разлики, което почти винаги е желаното поведение: потребител, който плъзга раздел, не е промяна на данни. Лист, наличен само от едната страна, отчита един запис на ниво лист, вместо да разширява всяка попълнена клетка вътре в него, което поддържа отчета за две структурно различни работни книги четим, вместо да е дълъг хиляди редове
Стойност или формула, и как всяка се сравнява
Всяка клетка допринася с подпис, а правилото е просто: клетка, носеща формула, се сравнява по текста на формулата с водещ знак за равенство, а клетка без такава се сравнява по стойността си, преобразувана в текст. Това разграничение има по-голямо значение, отколкото изглежда на пръв поглед. Две клетки могат да съдържат едно и също показано число, докато едната е литерал, а другата — формула, а третирането им като равни би скрило точно редакцията, най-важна за улавяне в прегледана работна книга
Това също означава, че формула, чийто текст е непроменен, не отчита разлика, дори ако кешираният ѝ резултат се различава, което е правилното поведение при сравняване на авторско съдържание, и грешното поведение, ако се опитвате да откриете отклонение при преизчисление. За този втори въпрос преизчислете и двете работни книги преди сравняване, така че сравняваните стойности да са тези, които формулите действително произвеждат днес
Изпълняване на сравнение
Compare приема двете заредени работни книги и връща броя намерени разлики. Списъкът с разлики след това е достъпен по индекс или може да бъде изсипан в произволен TStrings:
uses
lxHandleX, lxCompare;
var
Left, Right: TXLSXWorkbook;
Cmp: TXLSXWorkbookCompare;
Lines: TStringList;
begin
Left := TXLSXWorkbook.Create;
Right := TXLSXWorkbook.Create;
Cmp := TXLSXWorkbookCompare.Create;
Lines := TStringList.Create;
try
if (Left.Open('baseline.xlsx') <> 1) or
(Right.Open('reviewed.xlsx') <> 1) then
Exit;
if Cmp.Compare(Left, Right) = 0 then
Writeln('workbooks are equivalent')
else
begin
Cmp.Report(Lines); // по един четим ред за всяка разлика
Lines.SaveToFile('workbook-diff.txt');
Writeln(Format('%d difference(s) written', [Cmp.Count]));
end;
finally
Lines.Free;
Cmp.Free;
Right.Free;
Left.Free;
end;
end;
Ред, произведен от Report, изглежда като value changed: Data!A2: 10 -> 99, което е достатъчно за рецензент и достатъчно за commit съобщение. Това е повърхността, обърната към човека. Програмната повърхност е самият запис за разлика, и тя е тази, която трябва да се използва, когато сравнението захранва решение, а не документ
Управление на логиката от структурираните разлики
Всяка разлика излага своя вид, името на листа, ред и колона от 1 нагоре за записи на ниво клетка, A1 препратка за записи на ниво обединение, и левия и десния текст. Записите на ниво лист и на ниво обединение отчитат ред и колона като нула, което е начинът да ги различите без да проверявате вида:
var
I: Integer;
D: TlxCompareDiff;
FormulaEdits: Integer;
begin
FormulaEdits := 0;
for I := 0 to Cmp.Count - 1 do
begin
D := Cmp.Diff(I);
case D.Kind of
lckFormulaChanged:
begin
Inc(FormulaEdits);
Writeln(Format('%s R%dC%d: %s => %s',
[D.Sheet, D.Row, D.Col, D.LeftText, D.RightText]));
end;
lckSheetAdded, lckSheetRemoved:
Writeln(Format('structure: %s', [D.Describe]));
lckMergeAdded, lckMergeRemoved:
Writeln(Format('layout: %s at %s', [D.Describe, D.Ref]));
end;
end;
// Политика за преглед, която блокира само при редакции на формули
if FormulaEdits > 0 then
raise Exception.CreateFmt(
'%d formula change(s) need sign-off', [FormulaEdits]);
end;
Две свойства на изхода си струва да се знаят, преди да пишете твърдения спрямо него. Редът на записите на ниво клетка следва вътрешния ред на обхождане на хранилището за клетки, така че тестовете трябва да се пишат независимо от реда. А подписът на формулата носи свой собствен водещ знак за равенство, което означава, че низ за описание, изграден чрез конкатенация, може да покаже удвоено ==; проверявайте стойностите на полетата, а не анализирайте описателния ред, когато резултатът управлява логика
Къде сравняването на работни книги си заслужава
Три приложения оправдават функцията сами по себе си. Регресионно тестване на генератор на отчети: пазите известна добра работна книга, регенерирате, сравнявате и проваляте компилацията при всяка неочаквана разлика. Преглед на промени: подавате на рецензент четимия отчет вместо два файла. И проверка при миграция: след конвертиране на партида наследени работни книги, сравнявате всеки резултат спрямо източника му, за да докажете, че нищо не е загубено
Този трети случай се съчетава естествено с проходите за инвентаризация и одит, описани в работния плот за одит и конвертиране на работни книги, където преброяването на съдържанието на работна книга се случва преди конвертирането, а сравнението — след него. Ако вашите разлики се групират около вмъкнати редове, правилата за пренаписване на препратки в коригиране на препратки на формули при вмъкване и изтриване обясняват защо формули, които изглеждат непроменени, се отчитат като променени
Ограниченията, изложени ясно
Сравнението покрива стойности, формули, обединения и наличие на листове. То не сравнява числови формати, шрифтове, запълвания, правила за условно форматиране, проверки на данни, диаграми, изображения или дефинирани имена. Клетка, чиято стойност е идентична, но чийто формат се е променил от General в Currency, не отчита разлика, което е правилно за сравнение на данни и недостатъчно за преглед на форматирането
Клетки със стойности от тип дата заслужават едно конкретно предупреждение: те се сравняват по текстовото си преобразуване, така че работна книга, съхранена по системата от дати 1904, и такава по системата 1900, могат да се сравнят като равни или неравни по начини, които изненадват, ако основните серийни номера се различават. Правилата за системата на дати са разгледани в серийни номера на дати и системата 1904. Когато вярността на форматирането или на ниво обект е част от въпроса, съчетайте сравнението с одитен проход, който брои тези функции от всяка страна
Сравняването на работни книги, одитирането и конвертирането работят на един и същ двигател за Delphi и C++Builder; пълният списък с функции е на страницата на HotXLS Delphi компонента за електронни таблици