La question « ce classeur définit-il encore un nom appelé TaxRate ? » est répondue par une petite partie d'un paquet XLSX. Noms de feuilles, leur état caché ou très caché et chaque nom défini au niveau du classeur vivent dans xl/workbook.xml (ECMA-376 Part 1, §18.2), quelques kilooctets d'ordinaire. Les cellules vivent ailleurs, une partie xl/worksheets/sheetN.xml par feuille plus une table de chaînes partagées, et c'est là que se trouvent presque tous les octets. Un chargeur qui exige de bâtir le classeur complet avant que vous puissiez demander quoi que ce soit gonflera et analysera tout, pour chaque fichier du dossier, pour répondre à une question que les premiers kilooctets avaient déjà réglée
La scission qui compte est entre lire la forme d'un classeur et lire son poids. Un chargeur qui peut s'arrêter après la forme, charger le poids pour un sous-ensemble de feuilles choisi, et aller chercher une feuille de plus plus tard transforme « ouvrir un classeur » d'une étape tout-ou-rien en quelque chose que vous cadrez par appel. Il apporte aussi deux pièges avec lui, un sur les bases d'index et un sur la durée pendant laquelle le fichier source doit rester en place
Ce qu'un Open en métadonnées seules lit, et où il s'arrête
Avec TXLSXWorkbook.MetadataOnly posé à True avant Open, HotXLS analyse la partie classeur, les styles, le thème, les noms définis, les noms de feuilles et les états de visibilité, les connexions, les liens externes et les propriétés du document depuis docProps/core.xml et docProps/app.xml. Il saute ensuite le XML de cellules de chaque feuille et les parties satellites par feuille : commentaires, dessins et tableaux. La table de chaînes partagées est sautée aussi, ce qui compte plus qu'il n'y paraît, car dans les classeurs riches en texte cette seule partie peut rivaliser avec les feuilles en taille
Le classeur qui revient est structurellement complet. Sheets.Count est juste, chaque feuille a son vrai nom et sa Visibility, DefinedNames est peuplé, ExternalLinks liste chaque cible liée, et Title, Author et LastModifiedBy portent ce que le fichier a déclaré. Chaque feuille a simplement zéro cellule
C'est exactement les données sur lesquelles tourne un audit. Quels classeurs cachent une feuille ? Lesquels définissent encore TaxRate, et vers quoi ? Lesquels pointent un partage de fichiers retiré l'an dernier ? Le scanner ci-dessous répond aux trois pour un fichier. Notez les deux conventions d'index côte à côte : Sheets[] démarre à 1, comme le moteur XLS, tandis que DefinedNames et ExternalLinks sont des listes ordinaires à base 0
procedure ScanWorkbook(const FileName: WideString; Report: TStrings);
var
Workbook: TXLSXWorkbook;
Name: TXLSXDefinedName;
I: Integer;
begin
Workbook := TXLSXWorkbook.Create;
try
Workbook.MetadataOnly := True; // lu par Open, donc le poser d'abord
if Workbook.Open(FileName) <> 1 then
begin
Report.Add(FileName + ': open failed');
Exit;
end;
for I := 1 to Workbook.Sheets.Count do // Sheets[] démarre à 1
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 // liste à base 0
Report.Add(FileName + ': links to ' + Workbook.ExternalLinks[I].Target);
finally
Workbook.Free;
end;
end;
Le scanner lit Visibility plutôt que le booléen Visible à dessein. Visible est False pour les deux états cachés, et une feuille très cachée est celle qu'un audit veut le plus signaler, puisque la boîte de dialogue Unhide d'Excel ne la liste pas et que seul du VBA ou une édition directe du fichier la ramène
Charger deux feuilles sur quarante avec SelectedSheets
SelectedSheets est une TStringList possédée par le classeur. Quand elle tient au moins un nom, Open analyse le XML de cellules seulement pour les feuilles qu'il nomme ; toute autre feuille est créée avec son nom correct et sa visibilité et sans cellules. Une liste vide, le défaut, charge toutes les feuilles. Les noms sont cherchés avec le IndexOf propre à la liste, donc avec les réglages par défaut la correspondance ignore la casse, ce qui colle à la règle d'Excel selon laquelle deux noms de feuilles dans un même classeur ne peuvent pas différer rien que par la casse
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 est votre propre code UI
Les deux commutateurs n'interagissent que dans un sens. MetadataOnly = True passe outre la sélection et saute toutes les feuilles, donc un classeur configuré avec les deux revient sans aucune cellule nulle part, et la sélection paraît cassée alors qu'elle a simplement été devancée. Employez un seul par Open : métadonnées seules pour la découverte, une sélection pour du travail ciblé
Le chargement sélectif passe aussi par la phase d'analyse parallèle quand ParallelParse est actif ; les feuilles non sélectionnées sont sautées à l'intérieur des travaux de workers, donc les threads passent leur temps sur les feuilles demandées. Les deux mêmes propriétés s'appliquent quand la source est un classeur binaire .xlsb, avec une règle de cadrage : un .xlsb qui porte un projet VBA doit être ouvert sans ces deux options ; avec l'une quelconque posée, Open rapporte la lecture sélective comme non prise en charge
Pourquoi LoadSheet(1) charge-t-il la mauvaise feuille ?
Parce que LoadSheet prend une position à base 0 tandis que Sheets[] est à base 1. LoadSheet(1) matérialise la seconde feuille du classeur, et comme charger une feuille qui a déjà des cellules est un no-op qui renvoie True, un index mélangé peut avoir l'air d'un succès tandis que la feuille que l'utilisateur a cliquée reste vide. La conversion appartient à exactement un endroit de votre code
Le second piège est la durée de vie du fichier. LoadSheet ne garde pas l'archive ouverte entre les appels. Il rouvre le fichier d'origine par nom, analyse la partie classeur, les styles et les chaînes partagées, puis analyse la feuille demandée. Avant de se fier à ce fichier, HotXLS compare sa taille et son heure de dernière écriture avec les valeurs capturées à Open ; si un autre processus a sauvegardé par-dessus le classeur entre-temps, LoadSheet renvoie False au lieu de mélanger des cellules d'une autre version dans le modèle que vous tenez déjà
procedure TReportForm.ShowSheetNo(SheetNo: Integer); // SheetNo est à base 1, comme Sheets[]
var
Sheet: TXLSXWorksheet;
begin
Sheet := FWorkbook.Sheets[SheetNo];
if Sheet.Cells.Count = 0 then
if not FWorkbook.LoadSheet(SheetNo - 1) then // LoadSheet veut la position à base 0
begin
StatusBar.SimpleText := 'The workbook changed on disk; reopen it';
Exit;
end;
Grid.ShowSheet(Sheet); // votre propre code de grille
end;
Quelques conséquences découlent de ce design. Un classeur ouvert depuis un TStream n'a pas de fichier source vers qui revenir, donc LoadSheet renvoie False pour un .xlsx ouvert en flux ; il en va de même pour un classeur chiffré, qui est déchiffré en mémoire avant analyse. Un .xlsb binaire ouvert sélectivement depuis un flux fait exception, car HotXLS met en scène une copie temporaire du flux pour les feuilles différées. Et une feuille réellement vide sera relue à chaque appel, puisque zéro cellule est aussi à quoi ressemble une feuille sautée
Le modèle de coût décide de l'usage. Chaque appel LoadSheet réanalyse la table de chaînes partagées, donc matérialiser dix feuilles une par une paie cette partie dix fois. Pour une visionneuse où l'utilisateur ouvre un onglet de plus de temps en temps, c'est un prix correct. Quand vous connaissez déjà l'ensemble de feuilles, rouvrez avec SelectedSheets à la place : une passe d'archive, une analyse de chaînes partagées. Il y a aussi une différence de contenu : LoadSheet apporte les cellules, valeurs, formules et formats d'une feuille, tandis que commentaires, dessins et tableaux arrivent avec un Open, donc les feuilles dont votre code lit les commentaires ou tableaux appartiennent à la sélection d'avance
Un pipeline de découverte qui rouvre volontairement
Une sauvegarde qui régénère tout réécrit des octets qui n'ont jamais changé. Décompressez le thème, bâtissez un modèle objet, sérialisez-le, compressez-le de nouveau, et le résultat diffère de l'original bien que personne n'ait édité le thème. Pour les projets VBA et les caches de tableaux croisés, le coût dépasse le diff bruyant : un projet VBA est un fichier composé binaire avec ses propres flux internes, et un cache de tableau croisé tient un instantané de données sources dont les champs des définitions de tableau croisé sont adressés par position, donc la chose la plus sûre à faire avec l'un ou l'autre, quand personne ne l'a changé, est de laisser ses octets tranquilles
PreserveRawParts, qui vaut True par défaut, fait que SaveAs copie les octets compressés des parties inchangées droit depuis l'archive source. Cela couvre le thème, le projet VBA, les connexions, les graphiques, les médias incorporés, les réglages d'imprimante, les tableaux croisés et les définitions et enregistrements de caches de tableaux croisés, chacun seulement tant que le modèle dit qu'il n'a pas été modifié. Ces parties ne sont jamais gonflées ni regonflées en chemin. Poser la propriété à False force la re-sérialisation complète de chaque partie, le commutateur à atteindre quand vous voulez que la sortie ne reflète que ce que contient le modèle objet
La voie de copie brute emploie le même contrôle de fichier que LoadSheet : la source doit encore exister avec la taille et l'horodatage vus à Open. Quand ce n'est pas le cas, SaveAs sérialise ces parties normalement et le fichier reste complet. Deux cas prennent toujours la voie sérialisante : un classeur dont la source était un .xlsb binaire, converti en XML à la lecture, et une sauvegarde avec StrictOOXML posé, où les espaces de noms des parties changent
Sauvegarder un classeur partiellement chargé exige une décision. Pour une source .xlsb, SaveAs refuse tant qu'une feuille reste différée et enregistre « Load every deferred binary worksheet before saving » dans Diagnostics. Pour un .xlsx, traitez un classeur chargé sélectivement comme une vue en lecture : une feuille stub n'a pas de cellules à émettre pour le rédacteur, donc appelez LoadSheet pour chaque stub d'abord ou rouvrez sans sélection avant d'éditer et sauvegarder
Où cela s'intègre avec les autres leviers de gros fichiers
Le motif qui tient a deux passes. Passe un ouvre chaque fichier en métadonnées seules et décide. Passe deux ne rouvre que les fichiers qui correspondaient, avec une sélection de feuilles, et fait le travail. Rouvrir paraît du gâchis jusqu'à ce que vous comptiez ce que la passe un a évité : le second Open est le seul qui analyse des cellules, et il n'analyse que les feuilles que la passe un a choisies
procedure ProcessInvoiceFile(const FileName: WideString);
var
Probe, Workbook: TXLSXWorkbook;
Wanted: TStringList;
I: Integer;
begin
Wanted := TStringList.Create;
try
// Passe 1 : découverte, aucune cellule
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; // rien dans ce fichier ne vaut l'analyse
// Passe 2 : cellules pour les feuilles choisies seulement
Workbook := TXLSXWorkbook.Create;
try
Workbook.SelectedSheets.Assign(Wanted);
if Workbook.Open(FileName) = 1 then
PostInvoices(Workbook); // votre propre code de traitement
finally
Workbook.Free;
end;
finally
Wanted.Free;
end;
end;
Effacer MetadataOnly après Open ne transforme pas la sonde en classeur complet. Le drapeau est lu pendant l'ouverture ; le changer ensuite n'affecte que le prochain Open sur cet objet. La sonde peut quand même grandir une feuille à la fois via LoadSheet, et pour une ou deux feuilles c'est raisonnable, mais un pipeline est mieux servi par la réouverture propre, qui amène aussi les commentaires et tableaux de chaque feuille
Pourquoi un aller-retour change-t-il des parties que vous n'avez jamais touchées ?
Le chargement sélectif réduit ce que vous analysez. Il ne fait rien pour le coût d'une feuille une fois chargée, et il ne peut pas aider quand le travail exige chaque cellule de chaque feuille. Ce cas-là est une affaire de stockage, couverte dans l'article sur le stockage de cellules par blocs de lignes et les styles par plages, et de débit d'analyse, couvert dans l'analyse XLSX parallèle et l'allocateur mémoire. Quand la tâche est une lecture pure sans aucune édition, le lecteur direct en streaming saute entièrement le modèle objet du classeur et est le meilleur outil que toute stratégie de chargement qui en bâtit un
Tout cela tourne dans votre propre exécutable Delphi ou C++Builder, sans installation d'Excel sur le serveur qui scanne. Formats pris en charge, éditions et licences sont listés sur la page du composant tableur HotXLS Delphi