A HotXLS a TXLSXWorkbookCompare-en keresztül hasonlít össze két munkafüzetet, amely név szerint párosítja a munkalapokat, végigjárja minden pár feltöltött celláit, és eltérésrekordok strukturált listájaként, kérésre pedig eltérésenként egy olvasható sorként jelenti, mi tér el. Nincs szükség Excel-telepítésre, az összehasonlítás pedig teljes egészében a betöltött objektummodellen fut Delphiben vagy C++Builderben
Az igény általában akkor jelentkezik először, amikor valaki megkérdezi, mi változott. Egy pénzügyi munkafüzet visszaérkezik ellenőrzésről, egy éjszakai export egy kódváltoztatás után újragenerálódik, vagy két osztály ugyanannak a sablonnak eltérő verzióit küldi. A két fájl egymás mellett történő megnyitása egy munkalapnál működik, húsznál viszont csődöt mond. A fájlok bájtonkénti összehasonlítása semmit nem válaszol meg, mert ugyanannak a munkafüzetnek két mentése olyan módokon tér el, amelyekkel senki sem törődik
Mi számít eltérésnek?
Az összehasonlítás nyolc fajtát jelent, és a készlet szándékosan kicsi: hozzáadott vagy eltávolított munkalap, hozzáadott vagy eltávolított feltöltött cella, megváltozott értékű cella, megváltozott képletű cella, valamint hozzáadott vagy eltávolított egyesített tartomány. Minden a bal oldali munkafüzethez van kifejezve alapvonalként, így egy hozzáadott elem csak a jobb oldalon létezik, egy eltávolított elem pedig csak a bal oldalon
A munkalapok név szerint, nem pozíció szerint párosodnak. A munkalapok átrendezése ezért egyáltalán nem eredményez eltérést, ami szinte mindig a kívánt viselkedés: egy fülét húzgáló felhasználó nem adatváltozás. Egy csak az egyik oldalon jelen lévő munkalap egyetlen munkalapszintű bejegyzést jelent, ahelyett hogy kibontaná a benne lévő minden feltöltött cellát, ami olvashatóvá teszi két strukturálisan eltérő munkafüzet jelentését, ahelyett hogy több ezer sor hosszú lenne
Érték vagy képlet, és hogyan hasonlítja össze mindkettőt
Minden cella egy aláírással járul hozzá, a szabály pedig egyszerű: egy képletet hordozó cella a képletszövegét hasonlítja össze egy elöl álló egyenlőségjellel, egy anélküli cella pedig a szöveggé alakított értékét. Ez a megkülönböztetés jobban számít, mint elsőre tűnik. Két cella tarthatja ugyanazt a megjelenített számot, miközben az egyik literál, a másik pedig képlet, és egyenlőként kezelésük pontosan azt a szerkesztést rejtené el, amelyet a leginkább érdemes elkapni egy ellenőrzött munkafüzetben
Ez azt is jelenti, hogy egy változatlan szövegű képlet nem jelent eltérést akkor sem, ha a gyorsítótárazott eredménye eltér, ami helyes viselkedés a szerkesztett tartalom összehasonlításához, és helytelen, ha újraszámolási eltérést próbálsz felderíteni. Ehhez a második kérdéshez számold újra mindkét munkafüzetet összehasonlítás előtt, így azok az értékek kerülnek összehasonlításra, amelyeket a képletek ma valóban előállítanak
Egy összehasonlítás futtatása
A Compare a két betöltött munkafüzetet várja, és a talált eltérések számával tér vissza. Az eltéréslista ezután index szerint elérhető, vagy bármilyen TStrings-ba kiüríthető:
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); // eltérésenként egy olvasható sor
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;
A Report által előállított sor így néz ki: value changed: Data!A2: 10 -> 99, ami elég egy ellenőrnek, és elég egy commit üzenethez. Ez az emberek felé mutatott felület. A programozott felület maga az eltérésrekord, és ezt kell használni, amikor az összehasonlítás egy döntést táplál, nem egy dokumentumot
Logika vezérlése a strukturált eltérésekből
Minden eltérés kiteszi a fajtáját, a munkalap nevét, cellaszintű bejegyzéseknél az egytől induló sort és oszlopot, egyesítésszintű bejegyzéseknél egy A1-hivatkozást, valamint a bal és jobb oldali szöveget. A munkalapszintű és egyesítésszintű bejegyzések nullaként jelentik a sort és az oszlopot, így ezek alapján lehet őket megkülönböztetni a fajta vizsgálata nélkül:
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;
// Egy felülvizsgálati szabály, amely csak a képletszerkesztéseknél blokkol
if FormulaEdits > 0 then
raise Exception.CreateFmt(
'%d formula change(s) need sign-off', [FormulaEdits]);
end;
A kimenet két tulajdonságát érdemes ismerni, mielőtt állításokat írnál rá. A cellaszintű bejegyzések sorrendje a cellatároló belső bejárási sorrendjét követi, ezért a teszteket sorrendfüggetlenül kell megírni. A képlet-aláírás pedig saját elöl álló egyenlőségjelet hordoz, ami azt jelenti, hogy egy összefűzéssel felépített leíró string duplikált ==-t mutathat; ha az eredmény logikát vezérel, a mezőértékeket ellenőrizd, ne a leíró sort elemezd
Hol térül meg a munkafüzet-összehasonlítás?
Három felhasználás önmagában is indokolja a funkciót. Egy jelentéskészítő regressziós tesztelése: tarts meg egy ismerten jó munkafüzetet, generáld újra, hasonlítsd össze, és buktasd meg a buildet bármilyen váratlan eltérésnél. Változtatás-felülvizsgálat: adj át egy ellenőrnek olvasható jelentést két fájl helyett. És migrációellenőrzés: egy köteg örökölt munkafüzet konvertálása után hasonlítsd össze mindegyik eredményt a forrásával, hogy bizonyítsd, semmi nem veszett el
Ez a harmadik eset természetesen párosul a munkafüzet-audit és konverziós munkapadban leírt leltár- és auditfolyamatokkal, ahol a munkafüzet tartalmának megszámlálása a konverzió előtt, az összehasonlítás pedig utána történik. Ha az eltéréseid beszúrt sorok köré csoportosulnak, a képlethivatkozások módosítása beszúráskor és törléskor szereplő hivatkozás-átíró szabályok elmagyarázzák, miért jelentkeznek megváltozottként a változatlannak tűnő képletek
A korlátok, egyértelműen kimondva
Az összehasonlítás lefedi az értékeket, képleteket, egyesítéseket és a munkalapok jelenlétét. Nem hasonlítja össze a számformátumokat, betűtípusokat, kitöltéseket, feltételes formázási szabályokat, adatérvényesítéseket, diagramokat, képeket vagy elnevezett tartományokat. Egy azonos értékű, de Általánosról Pénznemre változtatott formátumú cella nem jelent eltérést, ami helyes egy adatösszehasonlításhoz, és elégtelen egy formázási felülvizsgálathoz
A dátumértékű cellák egy konkrét óvatosságot érdemelnek: szöveges átalakításukként hasonlítódnak össze, így egy 1904-es dátumrendszerben tárolt munkafüzet és egy 1900-as rendszerben tárolt munkafüzet meglepő módon egyezőnek vagy eltérőnek bizonyulhat, ha a mögöttes sorszámok eltérnek. A dátumrendszer-szabályokat a dátum-sorszámok és az 1904-es rendszer ismerteti. Ha a formázás vagy az objektumszintű hűség is a kérdés része, kombináld az összehasonlítást egy auditfolyamattal, amely mindkét oldalon megszámolja ezeket a funkciókat
A munkafüzet-összehasonlítás, az audit és a konverzió mind ugyanazon a motoron fut Delphihez és C++Builderhez; a teljes funkciólista a HotXLS Delphi táblázatkezelő-komponens oldalán található