HotXLS lit et écrit des fichiers Excel Binary Workbook (XLSB) depuis Delphi à travers la même façade TXLSXWorkbook que pour le XLSX : Open détecte un classeur binaire depuis le contenu du paquet, et SaveAs ou SaveAsChecked avec xlsxExcelBinaryWorkbook en écrit un. Le backend couvre un sous-ensemble validé et refuse ce qu'il ne peut pas représenter avant de toucher la destination
Cette dernière proposition est celle qu'il faut lire lentement. XLSB ressemble à une simple variation du XLSX, et en termes de paquetage c'est vrai. L'ennui vit dans les détails que les deux formats encodent autrement, et dans ce qu'une bibliothèque suppose tranquillement quand elle fait passer l'un pour l'autre. Le bug le plus instructif de ce backend n'a pas été du tout un enregistrement malformé. C'était une police par défaut qui disparaissait, et une colonne de dates qui devenait #### dans Excel alors que la valeur, le code de format et la largeur de colonne étaient tous corrects
Qu'est-ce qu'un fichier XLSB, et en quoi diffère-t-il du XLSX ?
Un fichier XLSB est un paquet OPC avec exactement la structure de parties d'un classeur XLSX, mais les parties classeur, feuille, style et chaînes partagées stockent des enregistrements binaires au lieu de XML. Le format est spécifié dans [MS-XLSB], et l'encodage des enregistrements est souvent appelé BIFF12. Vous avez toujours [Content_Types].xml, des parties de relations et un conteneur ZIP ; ce qui change, c'est que xl/workbook.bin remplace xl/workbook.xml, que xl/worksheets/sheet1.bin remplace le XML de feuille, et ainsi de suite
Chaque enregistrement BIFF12 démarre avec un en-tête compact à longueur variable. Le type d'enregistrement prend un ou deux octets, sept bits par octet avec le bit haut en drapeau de continuation, si bien qu'un type d'enregistrement tient en 14 bits. La taille de charge suit sur un à quatre octets avec le même schéma à sept bits. Les chaînes sont un compte de caractères 32 bits suivi d'unités de code UTF-16. Comparé au BIFF8, c'est une disposition bien plus propre : pas de découpage CONTINUE à 8224 octets, pas de drapeaux de chaîne compressée ou large au milieu de l'enregistrement. Comparé au XLSX, il saute entièrement la tokenisation XML, voilà pourquoi les gros fichiers XLSB ont tendance à être plus petits sur disque et plus rapides à ouvrir pour Excel
Un détail de paquetage fait trébucher le code de détection. Excel enregistre la partie classeur XLSB avec le type de contenu application/vnd.ms-excel.sheet.binary.macroEnabled.main, même pour des classeurs sans aucune macro. HotXLS vérifie d'abord cet enregistrement dans [Content_Types].xml et ne retombe sur l'extension de partie .bin que si le type de contenu est absent, et chacun des deux chemins doit quand même passer le validateur strict d'enregistrements. Les règles générales sur les noms de parties, les relations et les types de contenu sont celles couvertes dans pourquoi Excel répare un XLSX valide et les règles de paquet OPC derrière
Quand choisir XLSB plutôt que XLSX ?
Choisissez XLSB quand un système en aval l'exige ou quand vous échangez de gros classeurs riches en valeurs avec des utilisateurs d'Excel attentifs au temps d'ouverture ; sinon le XLSX reste le défaut plus sûr. Le XLSX est une norme ISO (ISO/IEC 29500) avec un large support d'outils, il est inspectable avec un outil ZIP et un éditeur de texte, et chaque produit tableur le lit. Le XLSB est un format Microsoft avec une spécification publiée mais un écosystème plus étroit
En pratique les demandes viennent de deux directions. Des pipelines de reporting et des archives se sont standardisés sur le .xlsb il y a des années et refusent tout le reste. D'autres équipes génèrent des classeurs avec des centaines de milliers de cellules numériques et ont constaté que les utilisateurs ouvrent la version binaire nettement plus vite. Ce sont des raisons légitimes. "Ça paraît plus efficace" n'en est pas une, parce que le backend binaire HotXLS met en attente le paquet complet en mémoire ; ce n'est donc pas l'outil pour des exports serveur à mémoire bornée. Pour ceux-là, le rédacteur XLSX en flux est le bon chemin
Ouvrir et enregistrer du XLSB avec TXLSXWorkbook
TXLSXWorkbook.Open n'a pas besoin d'indice de format : le même appel charge du XLSX, du XLSM ou du XLSB, et après un chargement binaire réussi TXLSXWorkbook.SourceFormat rapporte xlsxExcelBinaryWorkbook. L'écriture est explicite. SaveAs(FileName) sans format produit toujours un classeur Open XML, même quand le nom de fichier finit en .xlsb ; la sortie binaire nomme donc toujours le format
uses
SysUtils, lxHandleX;
procedure ConvertToXlsb(const Source, Target: string);
var
Book: TXLSXWorkbook;
begin
Book := TXLSXWorkbook.Create;
try
// Open détecte XLSX, XLSM ou XLSB depuis le paquet lui-même
if Book.Open(Source) <> 1 then
raise Exception.Create('Input is unsupported or invalid');
// L'extension seule ne choisit jamais la sortie binaire
if Book.SaveAsChecked(Target, xlsxExcelBinaryWorkbook) <> 1 then
raise Exception.Create('XLSB output is unsupported or failed');
finally
Book.Free;
end;
end;
La sortie en flux passe par SaveAs(Stream, xlsxExcelBinaryWorkbook) ou la surcharge en flux de SaveAsChecked. En interne, les deux routent vers les méthodes privées SaveAsXlsbStream et SaveAsXlsbFile ; vous ne les appelez jamais directement. La sortie fichier est mise en attente dans un fichier temporaire voisin et validée avec un unique remplacement final. Une destination en flux doit être lisible et positionnable, parce que le rédacteur photographie ses octets, sa taille et sa position et les restaure si la copie finale échoue. TXLSXWorkbook.LastSaveUsedStreaming rapporte False après un enregistrement binaire, ce qui est exact : le paquet entier existe en mémoire avant qu'un seul octet n'ait atteint votre destination
Pourquoi un sous-ensemble validé plutôt qu'une fidélité complète ?
HotXLS ne maintient pas un second modèle d'objet pour le XLSB. À l'ouverture, XlsbToOpenXml convertit le paquet binaire en paquet Open XML en mémoire et le parseur XML ordinaire le charge ; à l'enregistrement, le rédacteur XML construit un paquet et OpenXmlToXlsb l'encode en enregistrements binaires. Chaque fonction doit donc survivre à deux traductions, et une fonction que le pont ne peut pas traduire exactement est rejetée plutôt qu'approximée
L'ensemble pris en charge à partir de HotXLS 2.384.113 est assez large pour la plupart des classeurs de données : valeurs typées, chaînes partagées et en ligne, polices courantes, remplissages, bordures et formats de nombre, hauteurs de lignes et largeurs de colonnes, formules de cellules ordinaires avec résultats en cache, noms définis, cellules fusionnées, hyperliens, volets figés et fractionnés, plages AutoFilter avec critères personnalisés simples, et tables de feuille ordinaires. Restent dehors : formules partagées et matricielles, fonctions futures et définies par l'utilisateur, références à des classeurs externes, graphiques et dessins, PivotTables et slicers, VBA, mise en forme conditionnelle, validation de données et réglages de calcul non par défaut comme le calcul manuel ou l'itération
Le rejet est obligatoire, pas consultatif. TXLSXWorkbook.GetConversionReport(xlsConversionExcelBinaryWorkbook) liste les fonctions non prises en charge avant l'enregistrement, et ces entrées font échouer l'enregistrement même avec AAllowLossy = True ou un SaveAs sans vérification. C'est délibéré. Laisser tomber en silence une PivotTable d'une copie binaire est le genre de perte que personne ne remarque avant que le classeur ne soit devant un client
uses
Classes, lxHandleX, lxDiagnostics;
function CanSaveAsXlsb(Book: TXLSXWorkbook; Log: TStrings): Boolean;
var
Report: TXLSConversionReport;
I: Integer;
begin
Report := Book.GetConversionReport(xlsConversionExcelBinaryWorkbook);
try
Result := True;
for I := 0 to Report.Count - 1 do
if Report[I].Fidelity = xlsConversionUnsupported then
begin
Result := False;
Log.Add(Report[I].SheetName + ' ' + Report[I].Location + ': ' +
Report[I].Feature + ' - ' + Report[I].Message);
end;
finally
Report.Free; // l'appelant est propriétaire du rapport
end;
end;
Le rapport couvre le modèle vivant. La validation finale du paquet reste le dernier verrou pour ce que le modèle ne voit jamais, comme des enregistrements malformés ou des attributs inconnus dans le fichier source, si bien qu'un rapport propre suivi d'un enregistrement en échec est possible, et c'est le résultat de l'enregistrement sur lequel vous agissez
Pourquoi la police par défaut et le format 0.00 ont-ils disparu ?
La police par défaut et les formats de nombre intégrés ont disparu parce que le lecteur XML de styles faisait deux hypothèses qui tiennent pour les fichiers qu'HotXLS écrit lui-même mais pas pour les classeurs qu'Excel écrit. Le pont préservait parfaitement les deux ressources ; la perte arrivait une couche plus haut, dans le chemin public Open. Cet écart est la leçon principale de ce backend : un pont de format correct ne garantit pas un aller-retour public fidèle
La première hypothèse concernait fonts[0]. Quand HotXLS enregistre du XLSX, il écrit une police par défaut implicite dans la case zéro, et son lecteur XML saute cette case pour que la collection Fonts côté utilisateur démarre à l'indice zéro. Un XLSB écrit par Excel porte son propre défaut explicite dans cette case, et la sauter jetait ce choix. La seconde hypothèse concernait les formats de nombre. Le lecteur ne mappait que les valeurs numFmtId personnalisées (164 et au-delà) dans NumberFormats ; les ids intégrés restaient à zéro. Une cellule vide stylée portant le format intégré 2, qui est 0.00, revenait en General
Le correctif vit entièrement dans le chemin d'import binaire. Après que le paquet complet a passé le prévol, l'ouverture XLSB pose un mode de style privé pour le parse XML synchrone seulement. Dans ce mode, le lecteur charge chaque police, case zéro comprise, et décale chaque fontId de un, convertit chaque id intégré pris en charge en code de format explicite dans le modèle, et utilise quand même le numFmtId original pour la détection de dates, si bien qu'un format de date intégré garde sa sémantique de date. Un bloc finally efface toujours le mode, et un Open XLSX ou ODS ordinaire ultérieur efface aussi l'état de police par défaut retenu
Comment fonts[0] affecte-t-il les largeurs de colonnes Excel ?
Excel calcule les largeurs de colonnes depuis la police par défaut du classeur dans fonts[0], pas depuis les polices appliquées aux cellules de cette colonne. La width de colonne est stockée en unités de caractère de la largeur de chiffre maximale de la police par défaut, donc changer fonts[0] change la largeur en pixels de chaque colonne, colonnes dont les cellules utilisent toutes une autre police comprises
Cela a produit le symptôme le plus déroutant de toute l'enquête. Le classeur réenregistré avait des valeurs de cellules identiques, des codes de format identiques et des attributs width identiques, la police des cellules et le style Normal pointaient tous deux vers la police préservée, et Excel rendait quand même une colonne de dates en ####. Le fonts[0] écrit était revenu à Calibri 11, la largeur de chiffre maximale a rétréci, et les dates ne tenaient plus. HotXLS garde désormais la police par défaut source et la réécrit en fonts[0], en retombant sur Calibri 11 seulement pour les classeurs qui n'ont jamais eu de source binaire. Toute l'arithmétique des largeurs est dans la largeur de colonne Excel et la largeur de chiffre maximale (MDW) en Delphi
Les formats intégrés dépendant de la locale sont rejetés, pas devinés
Un id de format de nombre intégré sans code de format explicite n'est accepté que quand son sens est fixe ; les ids dépendant de la locale sont rejetés pendant le prévol du paquet. HotXLS mappe les ids 0 à 22, 37 à 40 et 45 à 49 vers des codes connus comme 0.00, 0%, mm:ss et @. Les ids restants sous 164 appartiennent à des formats dont le rendu dépend des réglages régionaux de l'installation Excel, et l'import les refuse avec le message "A locale-dependent number format requires an explicit format code"
Inventer un code pour ces ids serait pire qu'échouer. Un motif de date est-asiatique deviné a l'air plausible, fait des allers-retours sans erreur et affiche la mauvaise chose à la moitié de vos utilisateurs. Le timing compte aussi : la vérification tourne dans le prévol du paquet complet, avant que le classeur existant ne soit effacé, si bien qu'un fichier refusé laisse le modèle précédemment chargé, le nom de fichier et le format source intacts. L'article série de dates Excel et numFmt couvre comment les ids de dates intégrés interagissent avec les systèmes de dates 1900 et 1904
procedure LoadBinaryWorkbook(Book: TXLSXWorkbook; const FileName: string);
var
I: Integer;
begin
// L'entrée binaire demande un chargement complet immédiat ; les modes sélectifs refusent d'avance
Book.MetadataOnly := False;
Book.SelectedSheets.Clear;
if Book.Open(FileName) <> 1 then
begin
for I := 0 to Book.Diagnostics.Count - 1 do
Writeln(Book.Diagnostics[I].Message);
Exit; // le classeur précédemment chargé est encore intact
end;
// La sortie de formules demande un cache sauf si l'enregistrement recalcule
Book.RecalcBeforeSave := xrmAlways;
end;
Règles pratiques pour le XLSB dans du code de production
Le backend binaire est sûr à exploiter quand vous traitez ses rejets comme des données plutôt que comme des erreurs à étouffer. Quelques règles découlent du comportement ci-dessus
- Nommez le format à chaque enregistrement binaire ; l'extension
.xlsbseule produit toujours du XLSX - Lancez
GetConversionReport(xlsConversionExcelBinaryWorkbook)avant d'enregistrer des classeurs édités par des utilisateurs et montrez les fonctions non prises en charge à l'utilisateur - Laissez
MetadataOnlydésactivé etSelectedSheetsvide pour une entrée binaire, puisque le chargement paresseux demanderait de retenir une archive convertie - Donnez aux formules un résultat en cache ou recalculez avant d'enregistrer, parce qu'un cache manquant est rejeté sous
xrmManualcomme sous unxrmIfDirtypropre - Budgétez la mémoire pour le paquet converti complet plus la sortie binaire ; utilisez le XLSX en flux quand la mémoire doit rester bornée
Quand vous testez, comparez contre Excel à travers l'API publique plutôt qu'au niveau du pont. Les régressions de police par défaut et de 0.00 passaient toutes deux les tests du pont, et toutes deux apparaissaient immédiatement dans un classeur écrit par Excel ouvert puis réenregistré à travers Open et SaveAsChecked
FAQ
HotXLS détecte-t-il automatiquement les fichiers XLSB ?
Oui. TXLSXWorkbook.Open inspecte le type de contenu enregistré pour la partie classeur et valide les enregistrements binaires, donc le même appel charge du XLSX comme du XLSB, et SourceFormat rapporte lequel a été lu
Pourquoi un SaveAs avec un nom .xlsb écrit-il quand même du XLSX ?
HotXLS ne déduit jamais le format de sortie de l'extension. Passez xlsxExcelBinaryWorkbook à SaveAs ou SaveAsChecked pour écrire un classeur binaire
Que se passe-t-il quand un classeur utilise des fonctions hors du sous-ensemble XLSB ?
L'enregistrement échoue avant que la destination ne change, même avec l'enregistrement avec perte permis. GetConversionReport liste à l'avance les fonctions bloquantes, et Diagnostics explique l'échec ensuite
La prise en charge XLSB, le rapport de conversion et le reste de l'API de classeur sont livrés dans HotXLS, le composant tableur natif Delphi et C++Builder