Nogle gange er det eneste spørgsmål, en indtagsrutine skal have besvaret, strukturelt: har denne arbejdsbog et ark kaldet "Mapping", eller hvor mange faner indeholder den. At besvare det ved at kalde Open er den dyre måde at gøre det på. Et fuldt open udvider tabellen med delte strenge, afkoder hver eneste stilpost og gennemgår hvert regnearks celler, fordi det ikke har nogen måde at vide, at du kun ville have indholdsfortegnelsen. På en stor fil er det hundredvis af megabyte allokeringer og adskillige sekunders CPU-tid brugt på at læse en liste, der fylder nogle få kilobyte. HotXLS, det indfødte Delphi-regnearksbibliotek fra losLab, giver dig den liste helt af sig selv: GetSheetNames returnerer regnearksnavnene i arbejdsbogens rækkefølge uden at materialisere en eneste celle
Hvorfor kataloget er billigt at læse
Begge regnearksformater placerer deres indholdsfortegnelse tæt på begyndelsen, hvilket er det, der gør et listekald hurtigt frem for smart. En OOXML-pakke gemmer arkkataloget i xl/workbook.xml, en del, der forbliver lille, uanset om arbejdsbogen indeholder ti rækker eller ti millioner. En BIFF8 .xls gemmer sine BoundSheet-poster i begyndelsen af arbejdsbogens globals-stream, forud for alle celledata. Så det arbejde, et listekald undgår, er ikke en afrundingsfejl i forhold til et fuldt open. Det er det meste af filen. At læse kataloget koster den samme håndfuld kilobyte uanset antallet af rækker, mens et fuldt open skalerer med dataene, og på en arbejdsbog på flere megabyte løber den forskel op i flere størrelsesordener, både i berørte bytes og allokeret hukommelse
Den flade omkostning er den egenskab, det er værd at designe omkring. En indtagsspærre bygget på GetSheetNames opfører sig ens på en fil med 200 rækker og en på 200 MB, så den langsomste fil i en batch sætter ikke længere tempoet for at afgøre, om en fil overhovedet er værd at behandle
Ét kald på tværs af .xls, .xlsx og skabelonformaterne
På XLS-facaden læser TXLSWorkbook.GetSheetNames mere end .xls. Den accepterer også de zip-baserede .xlsx, .xlsm, .xltx og .xltm og trækker kun workbook.xml ud af arkivet. For ægte .xls-input scanner den BoundSheet-poster og stopper ved den første EOF-post i globals-substreamen, så en stor binær fil stadig kun koster sine indledende kilobyte. XLSX-facaden bærer en garanti, der betyder mere for langvarig servicekode, end det først lyder: TXLSXWorkbook.GetSheetNames efterlader arbejdsbogsinstansen hverken nulstillet eller udfyldt, så en instans, der allerede har et åbent dokument, kan undersøge andre filer uden at forstyrre den, den holder i hånden. GetODSSheetNames anvender den samme fremgangsmåde på OpenDocument-pakker, og hvert eneste af disse kald har en stream-overload, som lader dig inspicere en upload, der aldrig lander på disk
var
Book: TXLSXWorkbook;
Names: TStringList;
I: Integer;
begin
Names := TStringList.Create;
Book := TXLSXWorkbook.Create;
try
if Book.GetSheetNames('upload-7f3a.xlsx', Names) <= 0 then
raise Exception.Create('unreadable workbook package');
if Names.IndexOf('Mapping') < 0 then
raise Exception.Create('required Mapping sheet is missing');
for I := 0 to Names.Count - 1 do
Writeln(Format('sheet %d: %s', [I, Names[I]]));
finally
Book.Free;
Names.Free;
end;
end;
Det samme kald egner sig godt til en desktop-importdialog. List arkene, lad brugeren vælge ét, og betal først for det fulde open, når valget er truffet. Med en arbejdsbog på halvtreds ark er forskellen synlig: en vælger, der dukker op med det samme, over for én, der går i stå, mens hele filen indlæses bagved
Makroaktiverede .xlsm-filer og skabelonformaterne listes helt ligesom en almindelig .xlsx, da kataloget ligger i den samme workbook.xml, uanset om en vbaProject.bin følger med i pakken eller ej. En indtagspipeline kan derfor opremse arkene i en makroarbejdsbog til routing uden nogensinde at røre makro-payloaden og uden nogensinde at gøre noget, der ville køre den, og overlade makropolitik-afgørelsen til det trin, der rent faktisk åbner filen
Læs returværdien uden at snyde dig selv
Returkonventioner er ikke ensartede på tværs af HotXLS. Nogle kald returnerer 1 ved succes, andre returnerer et antal, så for listefunktionerne er det eneste tjek, der holder, at behandle enhver værdi på nul eller derunder som en fejl, med strenglisten ryddet. Modstå fristelsen til at læse en tom liste som "en arbejdsbog uden ark." Både ECMA-376 og BIFF8-specifikationen kræver mindst ét ark i en gyldig arbejdsbog, så nul navne betyder altid, at læsningen fejlede, aldrig at filen legitimt er tom
En mislykket listning er i sig selv et signal, det er værd at beholde. En .xlsx-fil, der fejler kaldet, er én af nogle få specifikke ting: afkortet, slet ikke en rigtig OOXML-pakke (fejlmærkede CSV-eksporter fra andre systemer dukker konstant op her), eller en krypteret container. At skelne mellem dem er det næste tjeks opgave. At logge de første bytes af den afviste fil sammen med fejlen gør som regel en supporttråd til en enkelt besked
Opdag krypterede containere, før du router
En krypteret .xlsx er ikke en zip. Det er en OLE compound-fil, der pakker EncryptionInfo- og EncryptedPackage-streams ind, så GetSheetNames kan ikke se ind i den og returnerer fejl som enhver anden ulæselig fil. CanReadEncrypted tester for den containerform, hvilket lader indtaget route en krypteret fil med vilje i stedet for at sluge en generisk læsefejl et sted dybt inde i en worker:
type
TIntakeRoute = (irNormal, irNeedsPassword, irUnreadable);
function ClassifyUpload(const FileName: string; Names: TStrings): TIntakeRoute;
var
Book: TXLSXWorkbook;
begin
Book := TXLSXWorkbook.Create;
try
// Krypteret OOXML er en OLE-container, ikke en zip: tjek dette først,
// fordi listekaldene ikke kan se ind i den.
if Book.CanReadEncrypted(FileName) then
Exit(irNeedsPassword);
if SameText(ExtractFileExt(FileName), '.ods') then
begin
if Book.GetODSSheetNames(FileName, Names) <= 0 then
Exit(irUnreadable);
end
else if Book.GetSheetNames(FileName, Names) <= 0 then
Exit(irUnreadable);
Result := irNormal;
finally
Book.Free;
end;
end;
Kryptering er der, hvor HotXLS bevidst er asymmetrisk, så routingen må respektere det. Ældre .xls-kryptering (RC4, RC4 CryptoAPI, XOR) kan læses: TXLSWorkbook.Open(FileName, Password) dekrypterer med en gemt adgangskode, og de filer kan blive på den automatiserede sti. Krypterede OOXML-pakker går den anden vej. HotXLS kan skrive én med SaveAsEncrypted, men den kan ikke læse en tilbage. OpenEncrypted rejser EXlsxEncryptionNotImplemented, når den får en krypteret pakke, hvilket er grunden til, at et ærligt indtagsdesign sender krypterede .xlsx til en person med Excel og holder den adgangskodebærende .xls i kode
Til batcharbejde vinder denne klassifikator sin plads ved at køre hen over en hel indkommende mappe, før nogen worker starter den egentlige behandling, da hver sondering koster omkring ét fil-open og nogle få kilobyte læsning. At lægge det først ændrer den fejltype, driften rent faktisk bekymrer sig om. I stedet for at et job kl. 3 om natten dør på fil 412 af 600, får du 412 filer i kø og 5 afvist ved indtag med en begrundelse knyttet til hver enkelt. Samme biblioteksfunktioner, langt bedre driftshistorie
De spørgsmål, et listekald ikke kan besvare
Navne og rækkefølge er hele det, du får. Listekaldene siger intet om synlighed, så skjulte og meget skjulte ark dukker op i listen og ser ud som alle de andre. De rapporterer ingen used-range-dimensioner, ingen celletællinger og ingen dokumentegenskaber. docProps/core.xml-delen er også lille, men der findes i dag ingen egenskabs-only-sondering, så forfatter- og titelmetadata koster stadig et fuldt Open. Den rene måde at leve med det på er at lade de billige facts route hver fil og gemme de dyre til filer, der overlever routingen. For de filer, der rent faktisk går videre til en dybdegående læsning, kører en skrivebeskyttet scanning af en stor .xls mærkbart hurtigere med _DisableGraphics := True, som springer OfficeArt-parsingen over. Bare gem aldrig fra den instans: tegnelaget, den sprang over, er væk fra modellen, og at gemme ville droppe det fra filen
Filer, der klarer triagen, går som regel videre til dybere analyse. Workbenchen til revision og konvertering af arbejdsbøger dækker de pr.-ark-tællere, det er værd at indsamle, når et fuldt open er berettiget, og guiden til ydelse i store arbejdsbøger dækker, hvordan man holder det fulde open hurtigt
HotXLS er et indfødt Object Pascal-regnearksbibliotek til Delphi og C++Builder; hele API-fladen, inklusive de inspektionskald, der vises her, er dokumenteret på HotXLS Delphi Component-produktsiden