HotXLS, izvorna Delphi i C++Builder Excel biblioteka, čita vrijednost koju je Excel već spremio uz formulu kroz TryGetCachedFormulaValue i IXLSFormulaCacheReader. Nijedna ulazna točka ne poziva kalkulator, ne dekompilira tokene formule, ne ažurira prljavo stanje ni ne zapisuje ništa natrag u model, pa radna knjiga koju samo čitate ostaje točno onakva kakvom ste je otvorili
Scenarij koji ovo pokreće dosadan je i izuzetno čest. Noćni posao otvori nekoliko stotina radnih knjiga koje je proizveo netko drugi, iz svake izvuče jedan stupac zbrojeva i gura brojeve u skladište podataka. Zbrojevi već leže u datotekama — Excel ih je izračunao i spremio. No trenutak kad posao upita ćeliju s formulom za njezinu vrijednost, biblioteka koja ima samo jedan odgovor na to pitanje gradi graf ovisnosti i vrednuje cijeli list, i posao koji je trebao biti vezan za I/O pretvori se u benchmark računanja
Zašto čitanje ćelije s formulom košta potpuni preračun?
Zato što je dohvatitelj vrijednosti na ćeliji s formulom zahtjev da se vrijednost proizvede, a jedini univerzalno ispravan način da se proizvede jest vrednovati formulu. To je pravo zadano ponašanje za aplikaciju koja uređuje radne knjige i krivo za cjevovod koji ih izvlači. Gore od toga, vrednovanje nije bez nuspojava: zapisuje rezultate natrag u ćelije, preokreće prljave zastavice i može se razriješiti drugačije od aplikacije koja je proizvela datoteku kada funkcija nije podržana ili je vanjska referenca slomljena. Posao koji ste operativnom timu opisali kao samo-čitanje tiho proizvodi radnu knjigu koja se više ne poklapa s onom na disku, i ako je bilo što kasnije spremi, datoteka na disku se također mijenja
Čitanje predmemoriranih vrijednosti druga je polovica ugovora. Odgovara na uže pitanje — što je aplikacija koja je proizvela datoteku ovdje spremila? — i odbija odgovoriti na bilo što drugo. Kada stvarno želite svježe brojeve, HotXLS i dalje nudi inkrementalni preračun vođen grafom ovisnosti; poanta jest da izvlačenje i vrednovanje budu dva različita poziva, a ne jedan poziv s dva raspoloženja
Tri ortogonalne činjenice o jednoj ćeliji
Najprije zaključak: predmemorirana vrijednost formule nosi tri neovisne činjenice, i njihovo stapanje u jedan Variant gubi informacije koje trebate. TXLSFormulaCacheInfo ih drži odvojene kao State, Kind i Value. TXLSFormulaCacheState bilježi podrijetlo kroz pet slučajeva — xlfcsNotFormula, xlfcsMissing, xlfcsLoaded, xlfcsCalculated i xlfcsInvalidated — dok TXLSFormulaCacheValueKind klasificira sadržaj kao xlfcvBlank, xlfcvNumber, xlfcvDateTime, xlfcvString, xlfcvBoolean ili xlfcvError. To razdvajanje jest ono što prisutnosti dopušta pošteno izvještavanje: predmemorirani prazan, predmemorirani prazan niz, predmemorirani False, predmemorirana nula i predmemorirana greška sve su stvarne vrijednosti, pa prisutnost nikada ne može biti zaključena iz VarIsEmpty ili VarIsNull. TryGetCachedFormulaValue vraća True samo za xlfcsLoaded i xlfcsCalculated, i i dalje puni dijagnostičko stanje kada vrati False
var
Book: TXLSXWorkbook;
Info: TXLSFormulaCacheInfo;
begin
Book := TXLSXWorkbook.Create;
try
Book.Open('quarterly-model.xlsx');
// SheetIndex, Row i Col svi imaju bazu jedan ovdje
if Book.TryGetCachedFormulaValue(1, 12, 5, Info) then
Writeln('cached value: ', VarToStr(Info.Value))
else
Writeln('no usable cache, state ordinal ', Ord(Info.State));
finally
Book.Free;
end;
end;
Zašto predmemorirana vrijednost nedostaje?
Postoji točno četiri razloga zašto TryGetCachedFormulaValue vraća False, i stanje vam kaže koji se primjenjuje. xlfcsNotFormula znači da ćelija drži literal ili ništa, i koordinate izvan raspona stape se u isti odgovor. xlfcsMissing znači da ćelija doista jest formula ali proizvođač nije spremio sadržaj vrijednosti za nju — čest ishod kada generator piše formule i pusti Excel da popuni rezultate pri prvom otvaranju. xlfcsInvalidated znači da je tekst formule zamijenjen nakon učitavanja, pa vrijednost koja je tamo nekad bila opisuje izraz koji više ne postoji. xlfcsCalculated, nasuprot tome, slučaj je uspjeha: označava vrijednost koju je vaš vlastiti kod ili HotXLS vrednovatelj proizveo tijekom ove sesije, za razliku od xlfcsLoaded, koji je došao iz datoteke
Iskrenost o nedostajućoj predmemoriji važnija je od njezina prekrivanja. HotXLS odbija izmisliti vrijednost, i pri spremanju je jednako strog — samo xlfcsLoaded i xlfcsCalculated ispisuju predmemoriranu vrijednost, dok xlfcsMissing i xlfcsInvalidated zapisuju samu formulu umjesto da zamrznu zastarjeli broj u datoteku. To vam ostavlja tri razumna odgovora u cjevovodu: preskočite redak i zabilježite prazninu, namjerno preračunajte tu jednu radnu knjigu i prihvatite trošak, ili vrednujte i uskladite. Ako se vrednovani broj ne slaže s onim što bi aplikacija koja je proizvela datoteku zapisala, tracer vrednovanja formula je alat za otkrivanje gdje su se dva računanja razdvojila, umjesto pogađanja iz rezultata
Jedan čitač za klasični, OOXML i ODF stroj
Cjevovod ne bi trebao mariti je li datoteka koju je upravo otvorio bila BIFF, OOXML ili ODF. IXLSFormulaCacheReader jest jedinstvena ulazna točka samo za čitanje za sve tri: i TXLSWorkbook.CreateFormulaCacheReader i TXLSXWorkbook.CreateFormulaCacheReader vraćaju lagan adapter nad rijetkim pretraživanjem ćelija koje svaki stroj već koristi, s identičnim koordinatama lista, retka i stupca s bazom jedan. Razredi radnih knjiga namjerno sami ne implementiraju sučelje — referenca sučelja na radnu knjigu promijenila bi njegovu semantiku vlasništva i pustila pozivatelje da prokližu pored najma trajanja. Umjesto toga, uništavanje radne knjige briše sirovi pokazivač unutar tog najma, i svaki čitač koji vaš kod još drži podiže EXLSFormulaCacheReaderInvalidated pri sljedećem upitu umjesto da dereferencira oslobođenu memoriju. To je fail-fast provjera trajanja, a ne jamstvo o istodobnosti
var
Reader: IXLSFormulaCacheReader;
Info: TXLSFormulaCacheInfo;
Row, Missing, Errors: Integer;
Total: Double;
begin
Reader := Book.CreateFormulaCacheReader;
Total := 0;
Missing := 0;
Errors := 0;
for Row := 2 to LastRow do
if Reader.TryGetCachedFormulaValue(1, Row, 7, Info) then
begin
case Info.Kind of
xlfcvNumber: Total := Total + Double(Info.Value);
xlfcvError: Inc(Errors);
end;
end
else if Info.State = xlfcsMissing then
Inc(Missing);
// Nijedan kalkulator nije radio, nijedna prljava zastavica se nije pomaknula, Book je nepromijenjen
end;
Gdje predmemorirani bajtovi stvarno žive
Za klasične .xls datoteke predmemorija je polje FormulaValue zapisa Formula, osam bajtova opisanih u [MS-XLS] §2.5.133. Kada je gornja riječ jednaka $FFFF, sadržaj nije IEEE 754 double nego označeni variant, i raspored je lako suptilno pokvariti: tip varianta sjedi u val[0] a boolean ili BErr sadržaj sjedi u val[2], uz nedefiniran val[1]. HotXLS je ranije čitao sadržaj iz val[1], što je vrsta pomaka baze koja izbije samo na specifičnim datotekama koje predmemoriraju boolean ili grešku umjesto broja. Čitač i pisac dijeljenih formula sada se slažu oko istih pomaka, pa predmemorirani TRUE preživi učitavanje i spremanje netaknut umjesto da truli u šum
Vjernost tipova u formatima paketa zaseban je problem sa svojom zamkom. U OOXML-u predmemorirana vrijednost visi na elementu c kao <v>, uz atribut t koji imenuje tip prema ECMA-376 Part 1 §18.3.1.4. HotXLS čita t="e" izravno u varError Variant i preslikava ga natrag u standardni tekst greške pri spremanju, pa greške nikada ne glume obične cijele brojeve — ali Delphi RTL vam ovdje neće pomoći, jer VarAsType(Integer, varError) podiže iznimku konverzije. Radna konstrukcija postavlja TVarData.VType i TVarData.VError izravno. Datumi slijede istu disciplinu u suprotnom smjeru: t="d" i ODF tip vrijednosti datuma izričite su deklaracije tipa i postaju varDate, dok BIFF numerička predmemorija uopće ne nosi zastavicu datuma i zato ostaje Double. HotXLS nikada ne pogađa datum iz brojčanog formata ćelije, jer je brojčani format prezentacija a predmemorija su podaci. ODF dodaje još jedan slučaj vrijedan znanja — office:value-type="void" izražava predmemoriju koja je prisutna ali ne nosi vrijednost, i budući da ODF nema tip vrijednosti greške, tekst koji liči na grešku čuva se kao tekst umjesto da se unagradi u grešku
function DescribeCache(const Info: TXLSFormulaCacheInfo): string;
begin
case Info.State of
xlfcsNotFormula: Result := 'not a formula cell';
xlfcsMissing: Result := 'formula stored with no cached value';
xlfcsInvalidated: Result := 'formula replaced since load';
else
case Info.Kind of
xlfcvError: Result := 'error code ' + IntToStr(TVarData(Info.Value).VError);
xlfcvDateTime: Result := DateTimeToStr(VarToDateTime(Info.Value));
xlfcvBoolean: Result := BoolToStr(Info.Value, True);
xlfcvNumber: Result := FloatToStr(Double(Info.Value));
xlfcvString: Result := VarToStr(Info.Value);
else
Result := 'present but blank';
end;
end;
end;
Dijele li dijeljene formule svoje predmemorirane vrijednosti?
Ne, i pretpostavka suprotnog način je na koji pretraga završi javljajući isti broj za cijeli stupac. OOXML dijeljena formula dijeli samo izraz formule i optimizaciju spremanja; svaka članska ćelija i dalje posjeduje svoj vlastiti <v>. HotXLS zato nikada ne širi predmemoriju korijenskog člana na sljedbenika koji je stigao bez vrijednosti, i sljedbenik koji je učitan kao xlfcsMissing i dalje javlja xlfcsMissing nakon spremanja i ponovnog otvaranja. Ako proučavate kako se grupa uopće sprema i širi, mehanika si atributa dijeljene formule i njegovo širenje pokrivena je odvojeno; za čitanje predmemorije pravilo se svodi na jedan redak — pitajte svaku ćeliju, ne vjerujte ničemu što niste tražili
Čitanje predmemoriranih vrijednosti, objedinjeni čitač među strojevima i stroj preračuna koji možete odabrati da ne pozovete svi stižu sa standardnom HotXLS Delphi Spreadsheet Component komponentom za Delphi i C++Builder, bez ovisnosti o Excelu ili bilo kojem OLE automation poslužitelju; stranica proizvoda nosi potpunu API referencu za ulazne točke radne knjige i čitača prikazane ovdje