La domanda "questo workbook definisce ancora un nome chiamato TaxRate?" è risposta di una piccola parte di un pacchetto XLSX. I nomi dei fogli, il loro stato nascosto o very-hidden e ogni defined name a livello workbook vivono in xl/workbook.xml (ECMA-376 Part 1, §18.2), di solito qualche kilobyte. Le celle vivono altrove, una parte xl/worksheets/sheetN.xml per foglio più una tabella shared-string, e lì sta quasi tutto il byte. Un loader che pretende di costruire il workbook completo prima che possiate chiedere qualcosa gonfia e analizza tutto, per ogni file nella cartella, per rispondere a una domanda che i primi kilobyte avevano già deciso
La spaccatura che conta è tra leggere la forma di un workbook e leggerne il peso. Un loader che può fermarsi dopo la forma, caricare il peso per un sottoinsieme scelto di fogli, e prendere un foglio in più più tardi trasforma "aprire un workbook" da un passo tutto-o-nulla in qualcosa che modulate per chiamata. Porta con sé anche due trappole, una sulle basi degli indici e una su quanto a lungo il file sorgente deve stare fermo
Cosa legge un Open metadata-only, e dove si ferma
Con TXLSXWorkbook.MetadataOnly impostato a True prima di Open, HotXLS analizza la parte workbook, gli stili, il tema, i defined names, i nomi e gli stati di visibilità dei fogli, le connessioni, i collegamenti esterni e le proprietà del documento da docProps/core.xml e docProps/app.xml. Poi salta l'XML delle celle di ogni foglio e le parti satellite per foglio: commenti, disegni e tabelle. Anche la tabella shared-string viene saltata, il che conta più di quanto suoni, perché nei workbook ricchi di testo quella parte può rivaleggiare con i fogli in dimensione
Il workbook che torna è strutturalmente completo. Sheets.Count è giusto, ogni foglio ha il suo vero nome e Visibility, DefinedNames è popolato, ExternalLinks elenca ogni destinazione collegata, e Title, Author e LastModifiedBy portano ciò che il file dichiarava. Ogni foglio semplicemente ha zero celle
È esattamente il dato su cui gira un audit. Quali workbook nascondono un foglio? Quali definiscono ancora TaxRate, e su cosa? Quali puntano a una condivisione di file dismessa l'anno scorso? Lo scanner sotto risponde a tutte e tre per un file. Notate le due convenzioni di indicizzazione fianco a fianco: Sheets[] è 1-based, come il motore XLS, mentre DefinedNames e ExternalLinks sono liste ordinarie 0-based
procedure ScanWorkbook(const FileName: WideString; Report: TStrings);
var
Workbook: TXLSXWorkbook;
Name: TXLSXDefinedName;
I: Integer;
begin
Workbook := TXLSXWorkbook.Create;
try
Workbook.MetadataOnly := True; // letto da Open, quindi impostatelo prima
if Workbook.Open(FileName) <> 1 then
begin
Report.Add(FileName + ': open failed');
Exit;
end;
for I := 1 to Workbook.Sheets.Count do // Sheets[] è 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 // lista 0-based
Report.Add(FileName + ': links to ' + Workbook.ExternalLinks[I].Target);
finally
Workbook.Free;
end;
end;
Lo scanner legge Visibility invece del Boolean Visible di proposito. Visible è False per entrambi gli stati nascosti, e un foglio very hidden è quello che un audit vuole più segnalare, dato che la finestra di dialogo Unhide di Excel non lo elenca e solo VBA o una modifica diretta del file lo riporta alla luce
Caricare due fogli su quaranta con SelectedSheets
SelectedSheets è una TStringList posseduta dal workbook. Quando tiene almeno un nome, Open analizza l'XML delle celle solo per i fogli che nomina; ogni altro foglio viene creato col suo nome e visibilità corretti e senza celle. Una lista vuota, il predefinito, carica ogni foglio. I nomi vengono cercati con l'IndexOf della lista stessa, quindi con le impostazioni predefinite la corrispondenza ignora le maiuscole, il che si allinea alla regola di Excel per cui due nomi di foglio in un workbook non possono differire solo per maiuscole
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 è il vostro codice UI
I due interruttori interagiscono in una sola direzione. MetadataOnly = True scavalla la selezione e salta tutti i fogli, quindi un workbook configurato con entrambi torna senza celle da nessuna parte, e la selezione sembra rotta quando è semplicemente stata scavalcata. Usatene uno per Open: metadata-only per la discovery, una selezione per il lavoro mirato
Il caricamento selettivo passa anche per la fase di parsing parallelo quando ParallelParse è attivo; i fogli non selezionati vengono saltati dentro i job dei worker, quindi i thread spendono il loro tempo sui fogli che avete chiesto. Le stesse due proprietà valgono quando la sorgente è un workbook binario .xlsb, con una regola di scope: un .xlsb che porta un progetto VBA deve essere aperto senza queste due opzioni; con una delle due impostata, Open riporta la lettura selettiva come non supportata
Perché LoadSheet(1) carica il foglio sbagliato?
Perché LoadSheet prende una posizione 0-based mentre Sheets[] è 1-based. LoadSheet(1) materializza il secondo foglio del workbook, e dato che caricare un foglio che ha già celle è un no-op che restituisce True, un indice confuso può sembrare un successo mentre il foglio su cui l'utente ha cliccato resta vuoto. La conversione sta in esattamente un punto del vostro codice
La seconda trappola è la vita del file. LoadSheet non tiene aperto l'archivio tra le chiamate. Riapre il file originale per nome, analizza la parte workbook, gli stili e le shared string, poi analizza il foglio richiesto. Prima di fidarsi di quel file, HotXLS confronta la sua dimensione e l'ora dell'ultima scrittura con i valori catturati alla Open; se nel frattempo un altro processo ha salvato sopra il workbook, LoadSheet restituisce False invece di mescolare celle di una versione diversa nel modello che già tenete
procedure TReportForm.ShowSheetNo(SheetNo: Integer); // SheetNo è 1-based, come Sheets[]
var
Sheet: TXLSXWorksheet;
begin
Sheet := FWorkbook.Sheets[SheetNo];
if Sheet.Cells.Count = 0 then
if not FWorkbook.LoadSheet(SheetNo - 1) then // LoadSheet vuole la posizione 0-based
begin
StatusBar.SimpleText := 'The workbook changed on disk; reopen it';
Exit;
end;
Grid.ShowSheet(Sheet); // il vostro codice griglia
end;
Da quel design seguono alcune conseguenze. Un workbook aperto da un TStream non ha un file sorgente a cui tornare, quindi LoadSheet restituisce False per un .xlsx aperto da stream; lo stesso vale per un workbook cifrato, che viene decifrato in memoria prima del parsing. Un .xlsb binario aperto selettivamente da stream è l'eccezione, perché HotXLS mette in scena una copia temporanea dello stream per i fogli rimandati. E un foglio genuinamente vuoto verrà riletto a ogni chiamata, dato che zero celle è anche l'aspetto di un foglio saltato
Il modello dei costi decide come usarlo. Ogni chiamata LoadSheet ri-analizza la tabella shared-string, quindi materializzare dieci fogli uno per uno paga quella parte dieci volte. Per un viewer dove l'utente apre un'altra scheda ogni tanto, è un prezzo buono. Quando sapete già il set di fogli, riaprite con SelectedSheets invece: un passaggio d'archivio, un parsing di shared-string. C'è anche una differenza di contenuto: LoadSheet porta le celle di un foglio, valori, formule e formati, mentre commenti, disegni e tabelle arrivano con un Open, quindi i fogli i cui commenti o tabelle il vostro codice legge stanno nella selezione fin dall'inizio
Perché un round trip cambia part che non hai mai toccato?
Un salvataggio che rigenera tutto riscrive byte che non sono mai cambiati. Decomprimete il tema, costruite un modello a oggetti, serializzatelo, ricomprimetelo, e il risultato differisce dall'originale anche se nessuno ha modificato il tema. Per i progetti VBA e i pivot cache il costo è più alto di un diff rumoroso: un progetto VBA è un compound file binario con i suoi stream interni, e un pivot cache tiene uno snapshot di dati sorgente i cui campi le definizioni della pivot table indirizzano per posizione, quindi la cosa più sicura da fare con uno dei due, quando nessuno l'ha modificato, è lasciare i suoi byte in pace
PreserveRawParts, che vale True per predefinito, fa copiare a SaveAs i byte compressi delle parti invariate dritte dall'archivio sorgente. Copre il tema, il progetto VBA, le connessioni, i grafici, i media incorporati, le impostazioni di stampa, le pivot table e le definizioni e i record dei pivot cache, ognuno solo mentre il modello dice che non è stato modificato. Quelle parti non vengono mai gonfiate né ri-sgonfiate lungo la strada. Impostare la proprietà a False forza la ri-serializzazione completa di ogni parte, che è l'interruttore da afferrare quando volete che l'output rifletta solo ciò che il modello a oggetti contiene
Il percorso di copia grezza usa lo stesso controllo file di LoadSheet: la sorgente deve ancora esistere con la dimensione e il timestamp visti alla Open. Quando non è così, SaveAs serializza quelle parti normalmente e il file resta comunque completo. Due casi prendono sempre la strada della serializzazione: un workbook la cui sorgente era un .xlsb binario, che viene convertito in XML alla lettura, e un salvataggio con StrictOOXML impostato, dove i namespace delle parti cambiano
Salvare un workbook caricato parzialmente vuole una decisione. Per una sorgente .xlsb, SaveAs rifiuta finché qualche foglio è ancora rimandato e registra "Load every deferred binary worksheet before saving" in Diagnostics. Per un .xlsx, trattate un workbook caricato selettivamente come una vista di lettura: uno stub di foglio non ha celle da emettere per il writer, quindi o chiamate LoadSheet per ogni stub prima o riaprite senza selezione prima di modificare e salvare
Una pipeline di discovery che riapre di proposito
Il pattern che tiene in piedi ha due passaggi. Il passaggio uno apre ogni file metadata-only e decide. Il passaggio due riapre solo i file che hanno corrisposto, con una selezione di fogli, e fa il lavoro. Riaprire sembra sprecone finché non contate ciò che il passaggio uno ha evitato: la seconda Open è l'unica che analizza celle, e analizza solo i fogli che il passaggio uno ha scelto
procedure ProcessInvoiceFile(const FileName: WideString);
var
Probe, Workbook: TXLSXWorkbook;
Wanted: TStringList;
I: Integer;
begin
Wanted := TStringList.Create;
try
// Passaggio 1: discovery, nessuna cella
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; // niente in questo file vale la pena analizzare
// Passaggio 2: celle solo per i fogli scelti
Workbook := TXLSXWorkbook.Create;
try
Workbook.SelectedSheets.Assign(Wanted);
if Workbook.Open(FileName) = 1 then
PostInvoices(Workbook); // il vostro codice di elaborazione
finally
Workbook.Free;
end;
finally
Wanted.Free;
end;
end;
Cancellare MetadataOnly dopo Open non trasforma il probe in un workbook completo. Il flag viene letto durante l'apertura; cambiarlo dopo influisce solo sulla prossima Open su quell'oggetto. Il probe può comunque far crescere un foglio alla volta tramite LoadSheet, e per uno o due fogli è ragionevole, ma una pipeline sta meglio con la riapertura pulita, che porta con sé anche i commenti e le tabelle di ogni foglio
Dove si incasta con le altre leve per file grandi
Il caricamento selettivo taglia ciò che analizzate. Non fa nulla per il costo di un foglio una volta caricato, e non può aiutare quando il job vuole ogni cella di ogni foglio. Quel caso riguarda lo storage, trattato nell'articolo su storage delle celle a blocchi di righe e stili per intervallo, e il throughput del parsing, trattato in parsing XLSX parallelo e allocatore di memoria. Quando il compito è una lettura pura senza alcuna modifica, il lettore diretto in streaming salta del tutto il modello a oggetti del workbook ed è lo strumento migliore di qualunque strategia di caricamento che ne costruisca uno
Tutto questo gira dentro il vostro eseguibile Delphi o C++Builder, senza installazione di Excel sul server che fa la scansione. Formati supportati, edizioni e licenze sono elencati nella pagina del componente foglio di calcolo Delphi HotXLS