HotXLS bere vire .xls, .xlsx, .xlsm, .ods, CSV in TSV prek enega samega vlečnega kazalca vrstic, TXLSRowCursor, katerega FindFirst in FindNext napredujeta eno logično vrstico naenkrat, v pomnilniku pa ostane le ta vrstica. Šestvrednostni stroj stanj ločuje pred-prvim od EOF, preklicanega in okvarjenega, starejši bralnik s povratnimi klici pa je zdaj vmesnik nad istim kazalcem
Scenarij je znan vsakomur, ki je oddal funkcijo uvoza. Pride 200-megabajtni .xlsx, priključite ročjevalnik OnCell, prva zahteva po »preberi ga« pa je »ustavi se po prvih sto reverznih knjiženjih«. Zdaj se oblika vaše kode bori z vami: zanka živi znotraj knjižnice, vaš ročjevalnik mora dvigniti zastavico, vsak nadaljnji povratni klic se še vedno sproži, dokler razčlenjevalnik ne opazi, nakopičeno stanje — koliko zadetkov doslej, kateri stolpec se je ujel, kaj napraviti naprej — pa mora živeti v poljih na razredu, ki obstaja samo zato, da povratnemu klicu da kam sede. Nič od tega ni težava razčlenjevanja. Je težava nadzora toka, in prav to odstrani vlečni kazalec
Kaj povratni klic potiska dejansko stane pri 200 MB
Potisk obrne nadzor, obrat pa je točno tisto, česar klicatelj, ki filtrira ali združuje, ne more privoščiti. Z API-jem povratnih klicev knjižnica lasti zanko, zato klicatelj ne more uporabiti Break, ne more preplesti dveh virov, ne more predati bralnika rutini, ki pričakuje, da bo gnana, in ne more izraziti »oglej si naslednjo vrstico, preden se odločiš«, brez medpomnjenja. Cena ni prepustnost — dobro napisana pot SAX povratnih klicev se pretaka v redu — je pa v tem, da vsak netrivialen porabnik izraste svoj majhen stroj stanj, da simulira zanko, ki je ni smel napisati. To pomnožite s štirimi formati datotek, vsak zgodovinsko s svojo vstopno točko pregledovanja, in semantika filtriranja, formul in napak začne med njimi razhajati — točno ta zdrs si je HotXLS zadala zapreti
Kako vlečni kazalec spremeni vaš klicajoč kodo?
Zanko vrne vam, s tem pa običajni nadzor toka Pascal. TXLSRowCursor.Open sprejme ime datoteke ali TStream, zazna format, enkrat naloži skupne nize in metapodatke o datumskem slogu ter izbere list 1. SelectSheet (z eniškim osnovanjem) ali SelectSheetByName ponovno usmeri na drug delovni list in ponastavi kazalec na pred-prvi. FindFirst in FindNext nato postavita na naslednjo naseljeno vrstico — vrstice brez dekodirljivih celic se preskočijo, zato se lahko RowIndex poskoki — trenutna vrstica pa je izpostavljena kot CellCount, Cells[] in ValueByCol[], vse eniško osnovane na osi stolpcev. Izstop iz zanke je Break
var
Cursor: TXLSRowCursor;
Hits: Integer;
begin
Cursor := TXLSRowCursor.Create;
try
Cursor.FirstRow := 2; // preskoči pas glave
Cursor.IncludeColumn(1); // dekodiraj le ta dva stolpca
Cursor.IncludeColumn(7);
if not Cursor.Open('postings-200mb.xlsx') then
Exit;
if not Cursor.SelectSheetByName('Ledger') then
Exit;
Hits := 0;
if Cursor.FindFirst then
repeat
if VarToStr(Cursor.ValueByCol[7]) = 'REVERSED' then
begin
Inc(Hits);
if Hits = 100 then
Break; // običajen Break; brez zastavice prekinitve, brez čuvaja
end;
until not Cursor.FindNext;
finally
Cursor.Free; // destruktor konča prehod
end;
end;
Projekcija in obseg sta nastavljena pred prehodom, ne filtrirana nato. FirstRow, LastRow, IncludeColumn, ClearColumnProjection, IncludeFormulaText, DetectDates in DetectTextTypes se vsi spoštujejo znotraj zaledij, tako da neizbrani stolpec svoje vrednosti, niza formule ali tovora bogatega besedila sploh ne dodeli — preizkusna zbirka regresij to dokaže s formulami 16 KiB in predpomnjenimi nizi, ki se nikoli ne materializirajo, ko njihov stolpec ni projiciran. Te možnosti so med dejavnim prehodom namenoma zamrznjene in spet postanejo zapisljive ob EOF, ob SelectSheet ali po Close, zato en pregled nikoli ne more zmešati dveh dekodirnih dogovorov. Če potrebujete le inventar listov in ne vrstic, je nalaganje samo metapodatkov in izbirnih listov cenejša vstopna točka
Eno zaledje na format, vsako s svojo zanko pregledovanja
Vsak format ima v HotXLS točno en napredni pregledovalnik, in vlečni kazalec ter bralnik s povratnimi klici oba gneta tisti isti pregledovalnik. TXLSXForwardRowBackend je edini SAX stroj stanj delovnega lista za dele listov ECMA-376 Part 1 §18.3, ki drži bralnik XML, tabelo skupnih formul in razčlenjevalnik bogatega besedila, in napreduje na točno eno fizično mejo <row> na klic. TXLSBiffForwardParser lasti globalne spremenljivke, izbiro listov in napredovanje vrstic za zapisni tok [MS-XLS]; da je bil naredljiv za pavzo, je dalo najostrejšo omejitev v celotni zasnovi, ker je predpomnjena nizovna formula zapis Formula, ki mu takoj sledi zapis String, torej točka vmesne zaustavitve na vrstico ne sme nikoli pristati med njima. TXLSForwardTextBackend drži bralnik, pozoren na BOM, dejavno ločilo in en logični zapis — CSV povoha vejico, podpičje, tabulator ali cev iz prvega zapisa, pri tem pa prezre znake v navedkih, večvrstična navedena polja pa se spojijo s #10, tako da številka vrstice sledi logičnim zapisom in ne fizičnim novim vrsticam. TXLSForwardOdsBackend drži eno samo fizično predlogo vrstice za tabele OpenDocument §9, table:number-rows-repeated obravnava kot preostali števec in ne kot razširitev, ter napreduje mimo pokritih celic brez izdajanja vrednosti. pretočni neposredni bralnik si deli isti nalagalnik skupnih nizov in datumskih slogov
Zakaj šest stanj namesto ene zastavice Eof?
Ker en sam boolean naredi štiri različne razmere nerazločljive in se klicatelji pri vseh zmotijo. TXLSRowCursorState jih poimenuje izrecno
xrcsClosed— noben vir ni odprtxrcsBeforeFirst— odprt ali ponovno usmerjen, še nobena vrstica prebranaxrcsActive— stoji na veljavni vrsticixrcsEof— list je porabljen do koncaxrcsCancelled— klicatelj je prehod namenoma ustavilxrcsFaulted— prehod je spodletel in izvirna izjema je bila sprožena
Zadnja ločnica je tista, ki šteje v produkciji. Manjkajoči del delovnega lista ali spodletel začetek prehoda obdrži svoj EReadError in premakne kazalec v xrcsFaulted; nikoli ni degradiran v goli False, ki bi ga klicatelj prebral kot »ta list je bil prazen«. Cancel je namenoma ožji od Close: zapre zaledje trenutnega delovnega lista in njegov podtok inflate ter razveljavi trenutno vrstico, ne sprosti pa arhiva ZIP niti izvornega toka, dvojni klic pa je brez učinka. Po preklicu nadaljujete z izrecnim klicem SelectSheet — kazalec prehoda ne bo tiho znova zagnal v vašem imenu. Lastništvo toka sledi istemu obrambnemu pravilu: xsoBorrowed je privzeto in ob zaprtju obnovi položaj toka, xsoOwned pa prenese lastništvo šele, ko je Open že uspel, zato spodletelo odpiranje nikoli ne sprosti toka, ki ga klicatelj še drži
var
Cursor: TXLSRowCursor;
Src: TFileStream;
begin
Src := TFileStream.Create('quarter.ods', fmOpenRead or fmShareDenyWrite);
try
Cursor := TXLSRowCursor.Create;
try
// xsoBorrowed: kazalec nikoli ne sprosti Src, Close pa obnovi
// položaj, ki ga je tok imel, ko je bil poklican Open
if not Cursor.Open(Src, xffAuto, xsoBorrowed) then
Exit;
if Cursor.FindFirst then
repeat
if UserPressedStop then
begin
Cursor.Cancel; // zapre le pogon delovnega lista in njegov
Break; // podtok inflate; idempotentno
end;
until not Cursor.FindNext;
case Cursor.State of
xrcsEof: Log('sheet consumed to the end');
xrcsCancelled: Log('stopped by the operator');
xrcsFaulted: Log('pass failed; the EReadError was already raised');
end;
finally
Cursor.Free;
end;
finally
Src.Free; // še vedno naš, še vedno veljaven, položaj obnovljen
end;
end;
Izposoja trenutne vrstice brez kopiranja
IXLSRowCursorView izroči vrstico drugi rutini, ne da bi podvajal tabelo celic. Pogled shrani deljenega varuha, ki hrani kazalec na kazalec in števec generacij UInt64; napredovanje, izbira lista, preklic, zapiranje in uničenje kazalca vsi povečajo to generacijo, uničenje pa poleg tega počisti lastnika varuha. Torej zastarel pogled ne more brati sproščenega pomnilnika: Valid je preizkus brez izjem, ki ga lahko pokličete kadarkoli, vsak drug član pa najprej preveri veljavnost in sproži EXLSRowCursorViewInvalidated. Bodite iskreni glede tega, kaj je ta dogovor — to je hitro odpovedoče preverjanje življenjske dobe, ne zagotovilo varnosti niti, in ne dovoljuje branja vrstice iz druge niti, medtem ko prva napreduje kazalec
var
View: IXLSRowCursorView;
Cell: TXLSRowCursorCell;
I: Integer;
begin
if Cursor.FindFirst then
repeat
View := Cursor.CurrentRowView; // izposoja; nobena tabela celic se ne kopira
for I := 0 to View.CellCount - 1 do
begin
Cell := View.Cells[I];
if Cell.HasFormula and not Cell.FormulaTextAvailable then
UseCachedResult(Cell.Value) // napredna branja BIFF obdržijo
else if Cell.Kind = xdkEmpty then // predpomnjen rezultat, ne žetonov
UseStyleOnly(Cell.StyleIndex) // Blank / MulBlank so prave celice
else
UseValue(Cell.Col, Cell.Value);
end;
until not Cursor.FindNext;
// Vmesnik preživi zanko, vrstica za njim pa ne
if not View.Valid then // Valid nikoli ne sproži izjeme; Cells[] pa bi jo zdaj
View := nil; // EXLSRowCursorViewInvalidated
end;
PeakRowBufferedBytes in kaj sme dokazati
PeakRowBufferedBytes obstaja, da pokaže, da pomnilnik sledi širini vrstice in ne številu vrstic. Nabira zapise celic, Variant, nize formul in tovora bogatega besedila trenutne izhodne vrstice ter vključi specifičen delovni nabor formata — logični zapis CSV, fizično predlogo vrstice ODS, vrh zapisov BIFF ali surovo celico XLSX, ki se pravkar dekodira. Berite ga skupaj s SheetPassesStarted, ki šteje, koliko prehodov delovnih listov se je res začelo. Dve opombi ohranjata to iskreno: številka je ocena, ne točno knjigovodstvo kopice, in je monotonska od zadnjega Open, zato je orodje za razhroščevanje in regresije in ne živi merilnik. Za širšo sliko, kam gre čas in bajti na zelo velikih zvezkih, glejte zmogljivost velikih delovnih zvezkov v Delphiju
Bralnik potiska je postal vmesnik, in česa kazalec ne bo storil
TXLSForwardReader ne nosi več ločenih vstopnih točk pregledovanja XLSX, BIFF in besedila. Nastavi kazalec, ga prehodi in trenutno vrstico prevede v dogodke OnSheet in OnCell, zato se dve fasadi ne moreta več razhajati pri filtriranju, stanju formul ali obravnavi napak. Dve posledici sta vredni poznavanja, preden nadgradite: povratni klic SheetIndex je zdaj na TXLSForwardReader povsod eniško osnovan (TXLSDirectReader obdrži svoj obstoječi dogovor dogodkov, osnovan na nič), OnSheet pa sproži pred SelectSheet, tako da nastavitev SkipSheet pomeni, da del delovnega lista sploh nikoli ni odprt ali dekomprimiran. Meje so enako izrecne: delovnega zvezka se med dejavnim prehodom ne sme spreminjati, preklic zahteva izrecen ponoven zagon, napredna pot BIFF pa nikoli ne dekompilira žetonov formul, zato klasične celice s formulami poročajo HasFormula true s FormulaTextAvailable false in vam izročijo predpomnjeni rezultat namesto da bi izmislile prazen niz formule. Vlečni kazalec in njegov vmesnik sta opravila 1.298 preizkusov na Delphi Win32 in Win64 ter statičnem paketu C++Builder 37.0 Win64
Če tehtate vlečni kazalec proti nalagalniku, ki ga imate zdaj, vprašanje ni, kateri se razčlenjuje hitreje, temveč kateri vam dovoli zapisati izstopni pogoj, ki ga dejansko potrebujete. Podrobnosti komponente, podprte različice IDE in licenciranje so na strani komponente preglednic HotXLS za Delphi