A HotXLS .xls, .xlsx, .xlsm, .ods, CSV és TSV forrásokat egyetlen pull sor-kurzorral, a TXLSRowCursor-ral olvas, amelynek FindFirst és FindNext metódusai egyszerre egy logikai sort léptetnek, miközben csak az a sor marad a memóriában. Egy hatértékű állapotgép különíti el a first előttit az EOF-tól, a megszakítotttól és a hibázottól, és a régebbi visszahívásos olvasó ma ugyanannak a kurzornak az adaptora
A forgatókönyv ismerős mindenki számára, aki szállított már importfunkciót. Beérkezik egy 200 MB-os .xlsx, beköti az Ön OnCell kezelőjét, és az olvasd el után az első követelmény az, hogy állj meg az első száz sztornózott tétel után. Most a kódja alakja harcol Ön ellen: a ciklus a könyvtárban él, a kezelőjének jelzőt kell emelnie, minden későbbi visszahívás tovább tüzel, amíg az elemző észre nem veszi, és a felgyülemlett állapot — eddig hány találat, melyik oszlop egyezett, mi a következő lépés — egy olyan osztály mezőiben kell éljen, amely csak azért létezik, hogy a visszahívásnak helyet adjon. Ebben semmi sem elemzési probléma. Vezérlésfolyam-probléma, és ez az, amelyet egy pull kurzor eltávolít
Mit költesz valójában egy push visszahívás 200 MB-on
A push megfordítja a vezérlést, és a megfordítás pontosan az, amit egy szűrő vagy összekapcsoló hívó nem engedhet meg magának. Visszahívásos API esetén a könyvtár birtokolja a ciklust, így a hívó nem használhat Break-et, nem fűzhet össze két forrást, nem adhatja az olvasót egy vezetést váró rutinnak, és nem fejezheti ki a tekints rá a következő sorra, mielőtt döntene pufferelés nélkül. A költség nem az áteresztőképesség — egy jól megírt SAX visszahívásos útvonal szépen streamel —, hanem az, hogy minden nem triviális fogyasztó saját kis állapotgépet növeszt, hogy szimulálja azt a ciklust, amelyet nem írhatott meg. Szorozza meg ezt négy fájlformátummal, amelyek mindegyike történetileg saját pásztázási belépési ponttal bír, és a szűrési, képlet- és hibaszemantika elkezdi szétválni köztük, ami pontosan az a sodródás, amelyet a HotXLS bezárni indult
Hogyan változtatja meg egy pull kurzor a hívó kódját?
Visszaadja Önnek a ciklust, és vele együtt a szokásos Pascal vezérlésfolyamot. A TXLSRowCursor.Open fájlnevet vagy TStream-et fogad, felismeri a formátumot, egyszer tölti be a megosztott sztringeket és dátumstílus-metaadatokat, és az 1. munkalapot választja. A SelectSheet (1 alapú) vagy a SelectSheetByName újracéloz egy másik munkalapot, és a kurzort first előtti állapotba állítja vissza. A FindFirst és FindNext ezután a következő feltöltött sorra pozicionál — a dekódolható cellákkal nem rendelkező sorok átugródnak, így a RowIndex ugorhat —, és az aktuális sor CellCount-ként, Cells[]-ként és ValueByCol[]-ként kerül felszínre, mind 1 alapú az oszloptengelyen. A ciklus elhagyása egy Break
var
Cursor: TXLSRowCursor;
Hits: Integer;
begin
Cursor := TXLSRowCursor.Create;
try
Cursor.FirstRow := 2; // a fejlécsáv átugrása
Cursor.IncludeColumn(1); // csak ezt a két oszlopot dekódolja
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; // közönséges Break; nincs abort jelző, nincs őrjel
end;
until not Cursor.FindNext;
finally
Cursor.Free; // a destruktor lezárja a menetet
end;
end;
A projekció és a tartomány a menet előtt állítódik be, nem utólag szűrődik. A FirstRow, LastRow, IncludeColumn, ClearColumnProjection, IncludeFormulaText, DetectDates és DetectTextTypes mind a backendek belsejében érvényesülnek, így egy ki nem választott oszlop először sem allokálja az értékét, képletsztringjét vagy rich-text terhelését — a regressziós készlet ezt 16 KiB-os képletekkel és gyorsítótárazott sztringekkel bizonyítja, amelyek sosem materializálódnak, amikor az oszlopuk nincs kivetítve. E lehetőségek szándékosan fagynak, amíg egy menet aktív, és EOF-nál, SelectSheet-nél vagy Close után újra írhatóvá válnak, így egy pásztázás sosem keverhet két dekódolási szerződést. Ha csak a munkalap-leltárra van szüksége, nem a sorokra, a csak metaadat és szelektív munkalapbetöltés az olcsóbb belépési pont
Formátumonként egy backend, mindegyiknek egy pásztázási ciklusa
Minden formátumnak pontosan egy előrenéző pásztázója van a HotXLS-en belül, és mind a pull kurzor, mind a visszahívásos olvasó ugyanazt a pásztázót hajtja. A TXLSXForwardRowBackend az egyetlen munkalap SAX állapotgép az ECMA-376 Part 1 §18.3 munkalaprészekhez, amely tartja az XML olvasót, a megosztott képlet táblát és a rich-text elemzőt, és hívásonként pontosan egy fizikai <row> határig lép. A TXLSBiffForwardParser birtokolja a globálisokat, a munkalapválasztást és a sorléptetést a [MS-XLS] rekord streamhez; felüggeszthetővé tétele adta a teljes tervezés legélesebb korlátját, mert egy gyorsítótárazott sztringes képlet egy Formula rekord, amelyet azonnal követ egy String rekord, így egy soronkénti felfüggesztési pont sosem landolhat a kettő között. A TXLSForwardTextBackend tart egy BOM-tudatos olvasót, az aktív elválasztót és egy logikai rekordot — a CSV az első rekordból szaglássza ki a vesszőt, pontosvesszőt, tabulátort vagy csövet, idézett karakterek figyelmen kívül hagyásával, és a több soros idézett mezők #10-nel fűzőnek össze, így a sorszám logikai rekordokat követ, fizikai újsorokat nem. A TXLSForwardOdsBackend egyetlen fizikai sor sablont tart az OpenDocument §9 táblákhoz, a table:number-rows-repeated-t maradó számként kezeli, kibontás helyett, és a lefedett cellákon lép át értékek kibocsátása nélkül. A streaming közvetlen olvasó ugyanazt a megosztott sztring- és dátumstílus-betöltőt osztja
Miért hat állapot egyetlen Eof jelző helyett?
Mert egyetlen boolean négy különböző helyzetet tesz megkülönböztethetetlenné, és a hívók mindről tévesen találgatnak. A TXLSRowCursorState explicit megnevezi őket
xrcsClosed— nincs nyitott forrásxrcsBeforeFirst— megnyitva vagy újracélozva, még egy sort sem olvastunkxrcsActive— érvényes soron állxrcsEof— a munkalap a végéig elfogyottxrcsCancelled— a hívó szándékosan leállította a menetetxrcsFaulted— a menet elbukott, és az eredeti kivétel eldobásra került
Ez az utolsó különbségtétel az, amely a termelésben számít. Egy hiányzó munkalaprész vagy egy elbukó menetindítás megtartja az EReadError-ját, és a kurzort xrcsFaulted-re mozgatja; sosem minősül le egyszerű False-dá, amelyet a hívó annak olvasna, hogy ez a munkalap üres volt. A Cancel szándékosan szűkebb, mint a Close: bezárja az aktuális munkalap backendet és annak felfújás alstreamjét, és érvényteleníti az aktuális sort, de nem bocsátja ki a ZIP archívumot vagy a forrás streamet, és kétszeri meghívása no-op. Egy megszakítás után a folytatáshoz explicit módon hívja a SelectSheet-et — a kurzor nem indít újra csendesen menetet az Ön nevében. A stream tulajdonlás ugyanazt a védő szabályt követi: az xsoBorrowed az alapértelmezés, és bezáráskor visszaállítja a stream pozícióját, az xsoOwned csak akkor ruház át tulajdont, amikor az Open már sikeres volt, így egy elbukó nyitás sosem szabadít fel olyan streamet, amelyet a hívó még tart
var
Cursor: TXLSRowCursor;
Src: TFileStream;
begin
Src := TFileStream.Create('quarter.ods', fmOpenRead or fmShareDenyWrite);
try
Cursor := TXLSRowCursor.Create;
try
// xsoBorrowed: a kurzor sosem szabadítja fel a Src-t, és a Close visszaállítja
// azt a pozíciót, amelyen a stream az Open hívásakor volt
if not Cursor.Open(Src, xffAuto, xsoBorrowed) then
Exit;
if Cursor.FindFirst then
repeat
if UserPressedStop then
begin
Cursor.Cancel; // bezárja a munkalap backendet és annak
Break; // felfújás alstreamjét csak; idempotens
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; // még mindig a miénk, még érvényes, pozíció visszaállítva
end;
end;
Az aktuális sor kölcsönzése másolás nélkül
Az IXLSRowCursorView egy másik rutinnak ad át egy sort a cellatömb duplikálása nélkül. A nézet egy megosztott őrt tárol, amely a kurzor pointert és egy UInt64 generációs számlálót tart; a léptetés, munkalapválasztás, megszakítás, bezárás és a kurzor megsemmisítése mind növeli azt a generációt, a megsemmisítés ráadásul törli az őr tulajdonosát. Így egy elavult nézet nem olvashat felszabadított memóriát: a Valid kivételmentes szonda, amelyet bármikor hívhat, minden más tag viszont először validál, és EXLSRowCursorViewInvalidated-t dob. Legyen őszinte azzal kapcsolatban, mi ez a szerződés — élettartami gyors hibázás, nem szálbiztonsági garancia, és nem jogosít fel arra, hogy egy második szálon olvasson sort, miközben az első lépteti a kurzort
var
View: IXLSRowCursorView;
Cell: TXLSRowCursorCell;
I: Integer;
begin
if Cursor.FindFirst then
repeat
View := Cursor.CurrentRowView; // kölcsönöz; nem másolódik cellatömb
for I := 0 to View.CellCount - 1 do
begin
Cell := View.Cells[I];
if Cell.HasFormula and not Cell.FormulaTextAvailable then
UseCachedResult(Cell.Value) // a BIFF előre olvasások megtartják a
else if Cell.Kind = xdkEmpty then // gyorsítótárazott eredményt, nem a tokéneket
UseStyleOnly(Cell.StyleIndex) // Blank / MulBlank valós cellák
else
UseValue(Cell.Col, Cell.Value);
end;
until not Cursor.FindNext;
// Az interfész túléli a ciklust, de mögötte a sor nem
if not View.Valid then // A Valid sosem dob; a Cells[] most dobna
View := nil; // EXLSRowCursorViewInvalidated
end;
PeakRowBufferedBytes, és amit bizonyítania szabad
A PeakRowBufferedBytes azért létezik, hogy bemutassa: a memória a sor szélességét követi, nem a sorok számát. Az aktuális kimeneti sor cellarekordjait, Variant-jait, képletsztringjeit és rich-text terheléseit halmozza, és beleszámítja a formátumspecifikus munkakészletet — a CSV logikai rekordot, az ODS fizikai sorsablont, a BIFF rekordcsúcsot vagy az éppen dekódolt XLSX nyers cellát. Olvassa együtt a SheetPassesStarted-del, amely azt számolja, hány munkalap menet indult el ténylegesen. Két kikötés tartja ezt őszintén: a szám becslés, nem pontos kupacszámadás, és a legutóbbi Open óta monoton, így hibakeresési és regressziós műszer, nem élő mérőóra. A hol mennek az idő és a bájtok nagyon nagy munkafüzeteken kérdés szélesebb képéhez lásd a nagy munkafüzet teljesítményét Delphiben
A push olvasó adaptorrá vált, és amit a kurzor nem tesz
A TXLSForwardReader már nem hordoz külön XLSX, BIFF és szöveg pásztázási belépési pontokat. Kurzort konfigurál, végigsétál rajta, és az aktuális sort OnSheet és OnCell eseményekké fordítja, ezért a két homlokzat már nem sodródhat szét a szűrés, képletállapot vagy hibakezelés dolgában. Két következményt érdemes ismerni a frissítés előtt: a visszahívás SheetIndex-e most egységesen 1 alapú a TXLSForwardReader-en (a TXLSDirectReader megtartja meglévő 0 alapú eseményszerződését), és az OnSheet a SelectSheet előtt tüzel, így a SkipSheet beállítása azt jelenti, hogy a munkalaprész sosem nyílik meg vagy bomlik ki egyáltalán. A határok ugyanilyen explicit: a munkafüzetet nem szabad módosítani, amíg menet aktív, a megszakítás explicit újraindítást követel, és a BIFF előre útvonal sosem dekompilál képlettokéneket, így a klasszikus képletcellák HasFormula igazat jelentenek FormulaTextAvailable hamissal, és a gyorsítótárazott eredményt adják, üres képletsztring kitalálása helyett. A sor-kurzor és adaptora 1 298 ellenőrzésen ment át Delphi Win32 és Win64, plusz a C++Builder 37.0 Win64 statikus csomag alatt
Ha pull kurzort mérlegel a mostani betöltője ellenében, a kérdés nem az, melyik elemz gyorsabban, hanem az, melyik teszi lehetővé, hogy megírja azt a kilépési feltételt, amelyet valójában szükséges. A komponens teljes részletei, a támogatott IDE verziók és a licencelés a HotXLS Delphi spreadsheet komponens oldalán találhatók