Techninis straipsnis

Dviejų Excel darbaknygių palyginimas Delphi aplinkoje su HotXLS

HotXLS palygina dvi darbaknyges per TXLSXWorkbookCompare, kuri suporuoja darbalapius pagal pavadinimą, apeina kiekvienos poros užpildytus langelius ir praneša, kas skiriasi, kaip struktūrizuotą skirtumų įrašų sąrašą, o paprašius – ir kaip po vieną skaitomą eilutę kiekvienam skirtumui. Jokia Excel diegtis nedalyvauja, o palyginimas vyksta visiškai su įkeltu objektų modeliu Delphi arba C++Builder aplinkoje

Poreikis paprastai atsiranda tada, kai kažkas pirmą kartą paklausia, kas pasikeitė. Finansų darbaknygė grįžta po peržiūros, kasnaktinis eksportas perkuriamas po kodo pakeitimo, arba du padaliniai atsiunčia to paties šablono versijas. Abu failus atidaryti greta veikia vienam lapui ir nebeveikia dvidešimčiai. Failų palyginimas baitas po baito neatsako į nieką, nes du to paties darbaknygės išsaugojimai skiriasi būdais, kurie niekam nerūpi

Kas laikoma skirtumu?

Palyginimas praneša aštuonias rūšis, ir šis rinkinys sąmoningai nedidelis: pridėtas arba pašalintas lapas, pridėtas arba pašalintas užpildytas langelis, langelis, kurio reikšmė pasikeitė, langelis, kurio formulė pasikeitė, ir pridėtas arba pašalintas sujungtas diapazonas. Viskas išreiškiama kairiosios darbaknygės kaip bazinės atžvilgiu, todėl pridėtas elementas egzistuoja tik dešinėje, o pašalintas – tik kairėje

Lapai suporuojami pagal pavadinimą, o ne pagal poziciją. Todėl darbalapių pertvarkymas iš vietos į vietą apskritai neduoda jokių skirtumų, o tai beveik visada yra tokia elgsena, kokios ir norite: vartotojas, tempiantis skirtuką, nėra duomenų pakeitimas. Lapas, esantis tik vienoje pusėje, praneša vieną lapo lygio įrašą, o ne išplečia kiekvieną jame užpildytą langelį, todėl dviejų struktūriškai skirtingų darbaknygių ataskaita lieka skaitoma, o ne tūkstančių eilučių ilgio

Reikšmė ar formulė, ir kaip kiekviena lyginama

Kiekvienas langelis suformuoja parašą, ir taisyklė paprasta: langelis su formule lyginamas pagal savo formulės tekstą su pradiniu lygybės ženklu, o langelis be jos lyginamas pagal savo reikšmę, paverstą tekstu. Šis skirtumas svarbesnis, nei atrodo iš pirmo žvilgsnio. Du langeliai gali rodyti tą patį skaičių, kai vienas yra literalas, o kitas – formulė, ir jų traktavimas kaip lygių paslėptų būtent tą redagavimą, kurį svarbiausia pastebėti peržiūrimoje darbaknygėje

Tai taip pat reiškia, kad formulė, kurios tekstas nepakito, skirtumo nepraneša, net jei jos talpykloje saugomas rezultatas skiriasi – tai teisinga elgsena lyginant sukurtą turinį, bet neteisinga elgsena, jei bandote aptikti perskaičiavimo nuokrypį. Antram klausimui perskaičiuokite abi darbaknyges prieš lygindami, kad lygintumėte reikšmes, kurias formulės iš tikrųjų duoda šiandien

Palyginimo vykdymas

Compare priima dvi įkeltas darbaknyges ir grąžina rastų skirtumų skaičių. Skirtumų sąrašas tada prieinamas pagal indeksą arba gali būti išpiltas į bet kokį 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);              // po vieną skaitomą eilutę kiekvienam skirtumui
      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;

Eilutė, kurią sukuria Report, atrodo kaip value changed: Data!A2: 10 -> 99, ir to pakanka peržiūrėtojui bei pakanka commit pranešimui. Tai žmogui skirtas paviršius. Programinis paviršius yra pats skirtumo įrašas, ir jį reikia naudoti, kai palyginimas maitina sprendimą, o ne dokumentą

Logikos valdymas iš struktūrizuotų skirtumų

Kiekvienas skirtumas atskleidžia savo rūšį, lapo pavadinimą, nuo vieneto skaičiuojamą eilutę ir stulpelį langelio lygio įrašams, A1 nuorodą sujungimo lygio įrašams, bei kairįjį ir dešinįjį tekstą. Lapo lygio ir sujungimo lygio įrašai eilutę ir stulpelį praneša kaip nulį – taip juos galima atskirti netikrinant rūšies:

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;

  // Peržiūros politika, blokuojanti tik dėl formulių pakeitimų
  if FormulaEdits > 0 then
    raise Exception.CreateFmt(
      '%d formula change(s) need sign-off', [FormulaEdits]);
end;

Dvi rezultato savybes verta žinoti prieš rašant patikrinimus (assertions) prieš jį. Langelio lygio įrašų tvarka seka vidinę langelių saugyklos apėjimo tvarką, todėl testai turėtų būti rašomi nepriklausomai nuo tvarkos. Ir formulės parašas neša savo pradinį lygybės ženklą, o tai reiškia, kad sujungimu sukurta apibūdinimo eilutė gali parodyti sudvigubintą ==; kai rezultatas valdo logiką, tikrinkite lauko reikšmes, o ne analizuokite apibūdinimo eilutę

Kur darbaknygių lyginimas atsiperka

Trys panaudojimo atvejai patys savaime pateisina šią funkciją. Ataskaitų generatoriaus regresijos testavimas: laikykite žinomą tinkamą darbaknygę, perkurkite, palyginkite ir sužlugdykite kūrimą dėl bet kokio netikėto skirtumo. Pakeitimų peržiūra: paduokite peržiūrėtojui skaitomą ataskaitą vietoj dviejų failų. Ir migracijos patikrinimas: konvertavus senų darbaknygių partiją, palyginkite kiekvieną rezultatą su jo šaltiniu, kad įrodytumėte, jog nieko neprarasta

Tas trečiasis atvejis natūraliai dera su inventorizacijos ir audito perėjimais, aprašytais straipsnyje darbaknygių audito ir konvertavimo darbastalis, kur darbaknygės turinio suskaičiavimas vyksta prieš konvertavimą, o palyginimas – po jo. Jei jūsų skirtumai telkiasi apie įterptas eilutes, nuorodų perrašymo taisyklės straipsnyje formulių nuorodų koregavimas įterpiant ir šalinant paaiškina, kodėl nepakitusiomis atrodančios formulės praneša kaip pakitusios

Ribos, pasakytos tiesiai

Palyginimas apima reikšmes, formules, sujungimus ir lapų buvimą. Jis nelygina skaičių formatų, šriftų, užpildų, sąlyginio formatavimo taisyklių, duomenų tikrinimo, diagramų, vaizdų ar apibrėžtų vardų. Langelis, kurio reikšmė identiška, bet formatas pasikeitė iš General į Currency, skirtumo nepraneša – tai teisinga duomenų palyginimui ir nepakankama formatavimo peržiūrai

Datos tipo langeliai nusipelno vienos konkrečios pastabos: jie lyginami pagal savo teksto konversiją, todėl darbaknygė, saugoma 1904 metų datos sistemoje, ir kita, saugoma 1900 metų sistemoje, gali lygintis kaip lygios arba nelygios būdais, kurie nustebins, jei pagrindiniai serijos numeriai skiriasi. Datos sistemos taisyklės aprašytos straipsnyje datos serijos numeriai ir 1904 sistema. Kai formatavimo ar objektų lygio tikslumas yra klausimo dalis, sujunkite skirtumų palyginimą su audito perėjimu, kuris kiekvienoje pusėje suskaičiuoja tas funkcijas

Darbaknygių palyginimas, auditas ir konvertavimas visi veikia tame pačiame variklyje Delphi ir C++Builder aplinkoms; pilnas funkcijų sąrašas pateikiamas HotXLS Delphi skaičiuoklės komponento puslapyje