HotXLS porównuje dwa skoroszyty za pomocą TXLSXWorkbookCompare, które paruje arkusze po nazwie, przechodzi wypełnione komórki każdej pary i zgłasza różnice jako uporządkowaną listę rekordów różnic oraz, na żądanie, jako jeden czytelny wiersz na różnicę. Nie jest wymagana żadna instalacja Excela, a porównanie działa całkowicie na wczytanym modelu obiektowym w Delphi lub C++Builder
Ta potrzeba zwykle pojawia się, gdy ktoś po raz pierwszy pyta, co się zmieniło. Skoroszyt finansowy wraca z przeglądu, nocny eksport jest regenerowany po zmianie kodu, albo dwa działy wysyłają wersje tego samego szablonu. Otwarcie obu obok siebie działa dla jednego arkusza i zawodzi dla dwudziestu. Porównywanie plików bajt po bajcie nie odpowiada na nic, ponieważ dwa zapisy tego samego skoroszytu różnią się w sposoby, którymi nikt się nie przejmuje
Co liczy się jako różnica?
Porównanie zgłasza osiem rodzajów różnic, a zbiór jest celowo mały: dodany lub usunięty arkusz, dodana lub usunięta wypełniona komórka, komórka, której wartość się zmieniła, komórka, której formuła się zmieniła, oraz dodany lub usunięty scalony zakres. Wszystko jest wyrażane względem lewego skoroszytu jako punktu odniesienia, więc element dodany istnieje tylko po prawej stronie, a usunięty tylko po lewej
Arkusze są parowane po nazwie, a nie po pozycji. Zmiana kolejności arkuszy nie daje więc w ogóle żadnych różnic, co niemal zawsze jest pożądanym zachowaniem: przeciągnięcie karty przez użytkownika nie jest zmianą danych. Arkusz obecny tylko po jednej stronie zgłasza jeden wpis na poziomie arkusza zamiast rozwijać każdą wypełnioną komórkę w jego wnętrzu, co utrzymuje raport dwóch strukturalnie różnych skoroszytów czytelny zamiast rozciągniętego na tysiące wierszy
Wartość czy formuła i jak każda z nich jest porównywana
Każda komórka wnosi sygnaturę, a reguła jest prosta: komórka niosąca formułę jest porównywana po tekście formuły z wiodącym znakiem równości, a komórka bez formuły jest porównywana po swojej wartości przekonwertowanej na tekst. To rozróżnienie ma większe znaczenie, niż wydaje się na pierwszy rzut oka. Dwie komórki mogą wyświetlać tę samą liczbę, gdy jedna jest literałem, a druga formułą, a potraktowanie ich jako równych ukryłoby dokładnie tę edycję, którą najbardziej warto wychwycić w recenzowanym skoroszycie
Oznacza to też, że formuła, której tekst się nie zmienił, nie zgłasza różnicy, nawet jeśli jej zbuforowany wynik jest inny, co jest poprawnym zachowaniem przy porównywaniu autorskiej treści, a błędnym, gdy próbuje się wykryć dryf ponownego przeliczenia. Dla tego drugiego pytania przelicz oba skoroszyty przed porównaniem, tak by porównywane wartości były tymi, które formuły faktycznie dają dzisiaj
Wykonywanie porównania
Compare przyjmuje dwa wczytane skoroszyty i zwraca liczbę znalezionych różnic. Lista różnic jest następnie dostępna po indeksie albo można ją zrzucić do dowolnego 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); // jeden czytelny wiersz na różnicę
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;
Wiersz wygenerowany przez Report wygląda jak value changed: Data!A2: 10 -> 99, co wystarcza dla recenzenta i wystarcza na wiadomość commita. To powierzchnia zwrócona ku człowiekowi. Powierzchnią programistyczną jest sam rekord różnicy i to jego należy używać, gdy porównanie zasila decyzję, a nie dokument
Sterowanie logiką na podstawie uporządkowanych różnic
Każda różnica udostępnia swój rodzaj, nazwę arkusza, liczony od jednego wiersz i kolumnę dla wpisów na poziomie komórki, odwołanie A1 dla wpisów na poziomie scalenia oraz tekst lewy i prawy. Wpisy na poziomie arkusza i na poziomie scalenia zgłaszają wiersz i kolumnę jako zero, po czym można je odróżnić bez sprawdzania rodzaju:
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;
// Zasada przeglądu blokująca tylko przy edycjach formuł
if FormulaEdits > 0 then
raise Exception.CreateFmt(
'%d formula change(s) need sign-off', [FormulaEdits]);
end;
Dwie właściwości wyniku warto znać, zanim napiszesz wobec niego asercje. Kolejność wpisów na poziomie komórki podąża za wewnętrzną kolejnością przechodzenia magazynu komórek, więc testy powinny być pisane niezależnie od kolejności. A sygnatura formuły niesie własny wiodący znak równości, co oznacza, że ciąg opisowy zbudowany przez konkatenację może pokazać podwójne ==; gdy wynik steruje logiką, sprawdzaj wartości pól, a nie parsuj wiersza opisowego
Gdzie porównywanie skoroszytów się opłaca
Trzy zastosowania same w sobie uzasadniają tę funkcję. Testowanie regresyjne generatora raportów: zachowaj znany dobry skoroszyt, zregeneruj, porównaj i zawal kompilację przy każdej nieoczekiwanej różnicy. Przegląd zmian: przekaż recenzentowi czytelny raport zamiast dwóch plików. I weryfikacja migracji: po konwersji partii starych skoroszytów porównaj każdy wynik z jego źródłem, aby udowodnić, że nic nie zginęło
Ten trzeci przypadek naturalnie łączy się z przebiegami inwentaryzacji i audytu opisanymi w warsztacie audytu i konwersji skoroszytów, gdzie liczenie zawartości skoroszytu odbywa się przed konwersją, a porównanie po niej. Jeśli różnice skupiają się wokół wstawionych wierszy, reguły przepisywania odwołań w dostosowywaniu odwołań formuł przy wstawianiu i usuwaniu wyjaśniają, dlaczego formuły wyglądające na niezmienione zgłaszają się jako zmienione
Ograniczenia, powiedziane wprost
Porównanie obejmuje wartości, formuły, scalenia i obecność arkuszy. Nie porównuje formatów liczb, fontów, wypełnień, reguł formatowania warunkowego, sprawdzania poprawności danych, wykresów, obrazów ani nazw zdefiniowanych. Komórka o identycznej wartości, ale ze zmienionym formatem z Ogólnego na Walutowy nie zgłasza różnicy, co jest poprawne dla porównania danych, a niewystarczające dla przeglądu formatowania
Komórki z wartościami daty zasługują na jedną konkretną przestrogę: są porównywane po swojej konwersji tekstowej, więc skoroszyt zapisany w systemie dat 1904 i taki w systemie 1900 mogą porównać się jako równe albo różne w sposób, który zaskoczy, jeśli leżące u podstaw liczby seryjne się różnią. Reguły systemu dat są opisane w liczbach seryjnych dat i systemie 1904. Gdy częścią pytania jest wierność formatowania lub wierność na poziomie obiektów, połącz diff z przebiegiem audytu, który liczy te funkcje po każdej stronie
Porównywanie skoroszytów, audyt i konwersja działają na tym samym silniku dla Delphi i C++Builder; pełna lista funkcji znajduje się na stronie komponentu arkusza kalkulacyjnego HotXLS dla Delphi