De vraag "definieert dit werkboek nog steeds een naam die TaxRate heet?" wordt beantwoord door één klein deel van een XLSX-package. Bladnamen, hun verborgen of zeer verborgen toestand en elke naam op werkboekniveau wonen in xl/workbook.xml (ECMA-376 Part 1, §18.2), meestal enkele kilobytes. De cellen wonen elders, één deel xl/worksheets/sheetN.xml per blad plus een tabel met gedeelde tekenreeksen, en daar zit vrijwel alle bytes. Een lader die erop staat eerst het volledige werkboek te bouwen voordat u iets mag vragen, zal alles inflaten en parsen, voor elk bestand in de map, om een vraag te beantwoorden die de eerste paar kilobytes al hadden opgelost
De splitsing die telt, is tussen het lezen van de vorm van een werkboek en het lezen van zijn gewicht. Een lader die kan stoppen na de vorm, het gewicht kan laden voor een gekozen subset van bladen, en later nog één blad kan ophalen, maakt van "werkboek openen" iets dat u per aanroep begrenst in plaats van een alles-of-niets-stap. Hij brengt ook twee valkuilen mee, één over indexbasen en één over hoe lang het bronbestand op zijn plek moet blijven
Wat een metadata-only Open leest, en waar hij stopt
Met TXLSXWorkbook.MetadataOnly op True gezet vóór Open, parst HotXLS het werkboekdeel, stijlen, theme, gedefinieerde namen, bladnamen en zichtbaarheidstoestanden, verbindingen, externe links en de documentproperties uit docProps/core.xml en docProps/app.xml. Daarna slaat hij de cel-XML van elk werkblad en de satellietdelen per blad over: comments, drawings en tabellen. De tabel met gedeelde tekenreeksen wordt ook overgeslagen, wat meer telt dan het klinkt, want in tekstzware werkboeken kan dat ene deel qua grootte tegen de werkbladen opboksen
Het werkboek dat terugkomt is structureel compleet. Sheets.Count klopt, elk blad heeft zijn echte naam en Visibility, DefinedNames is gevuld, ExternalLinks somt elke gelinkte target op, en Title, Author en LastModifiedBy dragen wat het bestand opgaf. Elk blad heeft simpelweg nul cellen
Dat is precies de data waar een audit op draait. Welke werkboeken verbergen een blad? Welke definiëren nog TaxRate, en waarnaartoe? Welke linken naar een bestandsshare die vorig jaar is opgeheven? De scanner hieronder beantwoordt alle drie voor één bestand. Let op de twee indexconventies naast elkaar: Sheets[] is 1-based, gelijk aan de XLS-engine, terwijl DefinedNames en ExternalLinks gewone 0-based lijsten zijn
procedure ScanWorkbook(const FileName: WideString; Report: TStrings);
var
Workbook: TXLSXWorkbook;
Name: TXLSXDefinedName;
I: Integer;
begin
Workbook := TXLSXWorkbook.Create;
try
Workbook.MetadataOnly := True; // wordt gelezen bij Open, dus eerst instellen
if Workbook.Open(FileName) <> 1 then
begin
Report.Add(FileName + ': open failed');
Exit;
end;
for I := 1 to Workbook.Sheets.Count do // Sheets[] is 1-based
case Workbook.Sheets[I].Visibility of
xlsxSheetHidden:
Report.Add(FileName + ': hidden sheet ' + Workbook.Sheets[I].Name);
xlsxSheetVeryHidden:
Report.Add(FileName + ': very hidden sheet ' + Workbook.Sheets[I].Name);
end;
Name := Workbook.DefinedNames.FindByName('TaxRate');
if Name <> nil then
Report.Add(FileName + ': TaxRate = ' + Name.Formula);
for I := 0 to Workbook.ExternalLinks.Count - 1 do // 0-based lijst
Report.Add(FileName + ': links to ' + Workbook.ExternalLinks[I].Target);
finally
Workbook.Free;
end;
end;
De scanner leest Visibility in plaats van de Boolean Visible, en wel met opzet. Visible is False voor beide verborgen toestanden, en een very hidden blad is precies het blad dat een audit het liefst vlagt, want het Unhide-dialoog van Excel somt hem niet op en alleen VBA of een directe bewerking van het bestand haalt hem terug
Twee bladen van veertig laden met SelectedSheets
SelectedSheets is een TStringList in eigendom van het werkboek. Houdt hij ten minste één naam vast, dan parst Open cel-XML alleen voor de bladen die hij benoemt; elk ander blad wordt aangemaakt met zijn correcte naam en zichtbaarheid en zonder cellen. Een lege lijst, de standaard, laadt elk blad. Namen worden opgezocht met de eigen IndexOf van de lijst, dus met de standaardinstellingen negeert de match hoofdletters, wat aansluit bij de regel van Excel dat twee bladnamen in één werkboek niet alleen in hoofdletters mogen verschillen
Workbook := TXLSXWorkbook.Create;
Workbook.SelectedSheets.Add('Summary');
Workbook.SelectedSheets.Add('Q3 Detail');
if Workbook.Open('consolidated-2026.xlsx') = 1 then
ShowSheet(Workbook.Sheets[1]); // ShowSheet is uw eigen UI-code
De twee schakelaars werken in één richting op elkaar in. MetadataOnly = True overschrijft de selectie en slaat alle bladen over, dus een werkboek dat met beide is geconfigureerd komt terug zonder cellen waar dan ook, en de selectie oogt kapot terwijl hij simpelweg werd overruleerd. Gebruik één per Open: metadata-only voor discovery, een selectie voor gericht werk
Selectief laden loopt ook door de parallelle parsefase wanneer ParallelParse aan staat; niet gekozen bladen worden binnen de workertaken overgeslagen, dus de threads besteden hun tijd aan de bladen die u vroeg. Dezelfde twee properties gelden wanneer de bron een binair .xlsb-werkboek is, met één reikwijdteregel: een .xlsb dat een VBA-project draagt moet zonder deze twee opties worden geopend; met één van beide gezet meldt Open de selectieve lezing als niet-ondersteund
Waarom laadt LoadSheet(1) het verkeerde blad?
Omdat LoadSheet een 0-based positie neemt terwijl Sheets[] 1-based is. LoadSheet(1) materialiseert het tweede blad in het werkboek, en omdat het laden van een blad dat al cellen heeft een no-op is die True teruggeeft, kan een verwisselde index er als succes uitzien terwijl het blad waarop de gebruiker klikte leeg blijft. Die conversie hoort thuis op precies één plek in uw code
De tweede valkuil is de levensduur van het bestand. LoadSheet houdt het archief niet open tussen aanroepen. Hij opent het originele bestand opnieuw bij naam, parst het werkboekdeel, stijlen en gedeelde tekenreeksen, en parseert daarna het gevraagde werkblad. Voordat hij dat bestand vertrouwt, vergelijkt HotXLS zijn grootte en laatste schrijftijd met de waarden die bij Open zijn vastgelegd; heeft een ander proces het werkboek intussen opnieuw opgeslagen, dan geeft LoadSheet False terug in plaats van cellen uit een andere versie te mengen in het model dat u al vasthoudt
procedure TReportForm.ShowSheetNo(SheetNo: Integer); // SheetNo is 1-based, zoals Sheets[]
var
Sheet: TXLSXWorksheet;
begin
Sheet := FWorkbook.Sheets[SheetNo];
if Sheet.Cells.Count = 0 then
if not FWorkbook.LoadSheet(SheetNo - 1) then // LoadSheet wil de 0-based positie
begin
StatusBar.SimpleText := 'The workbook changed on disk; reopen it';
Exit;
end;
Grid.ShowSheet(Sheet); // uw eigen gridcode
end;
Een paar gevolgen volgen uit dat ontwerp. Een werkboek dat vanuit een TStream is geopend, heeft geen bronbestand om naar terug te keren, dus LoadSheet geeft False terug voor een via stream geopend .xlsx; hetzelfde geldt voor een versleuteld werkboek, dat vóór het parsen in het geheugen wordt ontsleuteld. Een binair .xlsb dat selectief vanuit een stream wordt geopend is de uitzondering, want HotXLS stalt een tijdelijke kopie van de stream in voor de uitgestelde bladen. En een blad dat echt leeg is, wordt bij elke aanroep opnieuw gelezen, want nul cellen is ook hoe een overgeslagen blad eruitziet
Het kostenmodel bepaalt hoe u hem gebruikt. Elke LoadSheet-aanroep parst de tabel met gedeelde tekenreeksen opnieuw, dus tien bladen één voor één materialiseren betaalt voor dat deel tien keer. Voor een viewer waar de gebruiker nu en dan één extra tab opent, is dat een acceptabele prijs. Weet u de set bladen al, open dan opnieuw met SelectedSheets: één archiefdoorgang, één parse van de gedeelde tekenreeksen. Er is ook een inhoudelijk verschil: LoadSheet brengt de cellen, waarden, formules en formaten van een blad mee, terwijl comments, drawings en tabellen met een Open meekomen, dus bladen waarvan uw code de comments of tabellen leest, horen vooraf in de selectie
Waarom verandert een roundtrip delen die u nooit hebt aangeraakt?
Een opslag die alles opnieuw genereert, herschrijft bytes die nooit veranderden. Decomprimeer het theme, bouw een objectmodel, serialiseer het, comprimeer opnieuw, en het resultaat wijkt af van het origineel ook al heeft niemand het theme bewerkt. Voor VBA-projecten en pivot caches is de kost hoger dan een ruisige diff: een VBA-project is een binair compound file met zijn eigen interne streams, en een pivot cache bewaart een momentopname van brondata waarvan de velden door de definities van de draaitabel op positie worden aangesproken, dus het veiligste wat u met een van beide kunt doen wanneer niemand hem veranderde, is zijn bytes met rust laten
PreserveRawParts, dat standaard op True staat, laat SaveAs de gecomprimeerde bytes van ongewijzigde delen rechtstreeks uit het bronarchief kopiëren. Dat dekt het theme, het VBA-project, verbindingen, diagrammen, ingesloten media, printerinstellingen, draaitabellen en pivot cache-definities en -records, elk alleen zolang het model zegt dat hij niet is gewijzigd. Die delen worden onderweg nooit geïnflateerd of opnieuw gedeflateerd. De property op False zetten dwingt volledige herserialisatie van elk deel af, en dat is de schakelaar om te grijpen wanneer u wilt dat de uitvoer alleen weerspiegelt wat het objectmodel bevat
Het raw-copy-pad gebruikt dezelfde bestandscontrole als LoadSheet: de bron moet nog bestaan met de grootte en tijdstempel die bij Open werden gezien. Zo niet, dan serialiseert SaveAs die delen normaal en is het bestand alsnog compleet. Twee gevallen nemen altijd de serialiseerroute: een werkboek waarvan de bron een binair .xlsb was, dat bij het lezen naar XML wordt omgezet, en een opslag met StrictOOXML gezet, waarbij de namespaces van de delen veranderen
Een partieel geladen werkboek opslaan vraagt om een beslissing. Voor een .xlsb-bron weigert SaveAs zolang er nog een blad is uitgesteld en noteert "Load every deferred binary worksheet before saving" in Diagnostics. Voor een .xlsx: behandel een selectief geladen werkboek als een leesweergave; een stubblad heeft geen cellen voor de schrijver om uit te zenden, dus roep ofwel LoadSheet voor elke stub aan of open opnieuw zonder selectie voordat u bewerkt en opslaat
Een discoverypijplijn die met opzet opnieuw opent
Het patroon dat standhoudt heeft twee doorgangen. Doorgang één opent elk bestand metadata-only en beslist. Doorgang twee heropent alleen de bestanden die matchten, met een bladselectie, en doet het werk. Heropenen oogt verspillend totdat u telt wat doorgang één vermeed: de tweede Open is de enige die cellen parst, en hij parst alleen de bladen die doorgang één koos
procedure ProcessInvoiceFile(const FileName: WideString);
var
Probe, Workbook: TXLSXWorkbook;
Wanted: TStringList;
I: Integer;
begin
Wanted := TStringList.Create;
try
// Doorgang 1: discovery, geen cellen
Probe := TXLSXWorkbook.Create;
try
Probe.MetadataOnly := True;
if Probe.Open(FileName) <> 1 then
Exit;
for I := 1 to Probe.Sheets.Count do
if Pos('Invoice', Probe.Sheets[I].Name) > 0 then
Wanted.Add(Probe.Sheets[I].Name);
finally
Probe.Free;
end;
if Wanted.Count = 0 then
Exit; // niets in dit bestand is het parsen waard
// Doorgang 2: cellen voor alleen de gekozen bladen
Workbook := TXLSXWorkbook.Create;
try
Workbook.SelectedSheets.Assign(Wanted);
if Workbook.Open(FileName) = 1 then
PostInvoices(Workbook); // uw eigen verwerkingscode
finally
Workbook.Free;
end;
finally
Wanted.Free;
end;
end;
MetadataOnly na Open weer wissen maakt van de probe geen volledig werkboek. De vlag wordt bij het openen gelezen; hem daarna veranderen raakt alleen de volgende Open op dat object. De probe kan nog steeds blad voor blad groeien via LoadSheet, en voor één of twee bladen is dat redelijk, maar een pijplijn wordt beter geholpen door de schone heropening, die ook de comments en tabellen van elk blad meebrengt
Waar dit past tussen de andere hefbomen voor grote bestanden
Selectief laden snijdt weg wat u parst. Hij doet niets voor de kost van een blad eenmaal geladen, en hij kan niet helpen wanneer de klus elke cel van elk blad nodig heeft. Dat geval gaat over opslag, behandeld in het artikel over rijblok-celopslag en op bereik gebaseerde stijlen, en over parsethroughput, behandeld in parallelle XLSX-parsing en de geheugenallocator. Is de taak een pure lezing zonder enige bewerking, dan slaat de streamed directe lezer het werkboekobjectmodel helemaal over en is hij het betere gereedschap vergeleken met elke laadstrategie die er een bouwt
Dit alles draait binnen uw eigen Delphi- of C++Builder-executable, zonder Excel-installatie op de server die scant. Ondersteunde formaten, edities en licenties staan vermeld op de HotXLS Delphi-spreadsheetcomponentpagina