Teknisk artikel

Läsa och skriva XLSB-binära arbetsböcker i Delphi

HotXLS läser och skriver Excel Binary Workbook-filer (XLSB) från Delphi via samma TXLSXWorkbook-fasad som för XLSX: Open detekterar en binär arbetsbok ur paketinnehållet, och SaveAs eller SaveAsChecked med xlsxExcelBinaryWorkbook skriver en. Backend:en täcker en validerad delmängd och avvisar allt den inte kan representera innan den vidrör destinationen

Den sista klausulen är delen värd att läsa långsamt. XLSB ser ut som en enkel variant av XLSX, och i pakettermer är det den. Problemen bor i detaljerna de två formaten kodar olika, och i vad ett bibliotek tyst antar när det broar det ena in i det andra. Den mest lärorika buggen i den här backend:en var inte en felformad post alls. Det var ett standardtypsnitt som försvann, och en datumkolumn som blev #### i Excel fast värdet, formatkoden och kolumnbredden alla var korrekta

Vad är en XLSB-fil, och hur skiljer den sig från XLSX?

En XLSB-fil är ett OPC-paket med exakt den delstruktur en XLSX-arbetsbok har, men arbetsboks-, kalkylblads-, stil- och delade-sträng-delarna lagrar binära poster i stället för XML. Formatet är specificerat i [MS-XLSB], och postkodningen kallas ofta BIFF12. Du får fortfarande [Content_Types].xml, relationsdelar och en ZIP-container; det som ändras är att xl/workbook.bin ersätter xl/workbook.xml, xl/worksheets/sheet1.bin ersätter kalkylblads-XML:n, och så vidare

Varje BIFF12-post börjar med ett kompakt huvud med variabel längd. Posttypen tar en eller två bytes, sju bitar per byte med högsta biten som fortsättningsflagga, så en posttyp ryms i 14 bitar. Nyttlaststorleken följer i en till fyra bytes med samma sjubitarschema. Strängar är ett 32-bitars teckenantal följt av UTF-16-kodenheter. Jämfört med BIFF8 är det en mycket renare layout: ingen 8224-bytes-CONTINUE-splittring, inga komprimerad-mot-bred-sträng-flaggor mitt i posten. Jämfört med XLSX hoppar den över XML-tokenisering helt, vilket är varför stora XLSB-filer tenderar att vara mindre på disk och snabbare för Excel att öppna

En paketdetalj snubblar detekteringskod. Excel registrerar XLSB-arbetsboksdelen med content type application/vnd.ms-excel.sheet.binary.macroEnabled.main, även för arbetsböcker som inte innehåller några makron. HotXLS kontrollerar den registreringen i [Content_Types].xml först och faller bara tillbaka på .bin-delextensionen när content type saknas, och endera vägen måste fortfarande klara den strikta postvalidatorn. De allmänna reglerna om delnamn, relationer och content types är samma som täcks i varför Excel reparerar en giltig XLSX och OPC-paketreglerna bakom det

När ska du välja XLSB före XLSX?

Välj XLSB när ett nedströmssystem kräver det eller när du utväxlar stora värdestunga arbetsböcker med Excel-användare som bryr sig om öppningstid; annars förblir XLSX det säkrare standardvalet. XLSX är en ISO-standard (ISO/IEC 29500) med brett verktygsstöd, den är inspekterbar med ett ZIP-verktyg och en textredigerare, och varje kalkylarksprodukt läser den. XLSB är ett Microsoft-format med en publicerad specifikation men ett smalare ekosystem

I praktiken kommer förfrågningarna från två håll. Vissa rapporteringspipelines och arkiv standardiserade på .xlsb för år sedan och avvisar allt annat. Andra team genererar arbetsböcker med hundratusentals numeriska celler och fann att användare öppnar den binära versionen märkbart snabbare. Båda är legitima skäl. "Det låter mer effektivt" är det inte, eftersom HotXLS binära backend mellanlagrar hela paketet i minnet, så den är inte verktyget för serverexporter med begränsat minne. För dem är den strömmande XLSX-skrivaren rätt väg

Öppna och spara XLSB med TXLSXWorkbook

TXLSXWorkbook.Open behöver inget formattips: samma anrop laddar XLSX, XLSM eller XLSB, och efter en lyckad binär laddning rapporterar TXLSXWorkbook.SourceFormat xlsxExcelBinaryWorkbook. Skrivning är explicit. SaveAs(FileName) utan format producerar alltid en Open XML-arbetsbok, även när filnamnet slutar på .xlsb, så binär utdata namnger alltid formatet

uses
  SysUtils, lxHandleX;

procedure ConvertToXlsb(const Source, Target: string);
var
  Book: TXLSXWorkbook;
begin
  Book := TXLSXWorkbook.Create;
  try
    // Open detekterar XLSX, XLSM eller XLSB ur paketet självt
    if Book.Open(Source) <> 1 then
      raise Exception.Create('Input is unsupported or invalid');

    // Enbart extensionen väljer aldrig binär utdata
    if Book.SaveAsChecked(Target, xlsxExcelBinaryWorkbook) <> 1 then
      raise Exception.Create('XLSB output is unsupported or failed');
  finally
    Book.Free;
  end;
end;

Strömutdata går via SaveAs(Stream, xlsxExcelBinaryWorkbook) eller strömöverladdningen av SaveAsChecked. Internt dirigerar båda till de privata metoderna SaveAsXlsbStream och SaveAsXlsbFile; du anropar dem aldrig direkt. Filutdata mellanlagras i en syskon-temporärfil och committas med en enda slutlig ersättning. En strömdestination måste vara läsbar och sökbar, eftersom skrivaren snapshot:ar dess bytes, storlek och position och återställer dem om den slutliga kopieringen misslyckas. TXLSXWorkbook.LastSaveUsedStreaming rapporterar False efter en binär sparning, vilket är korrekt: hela paketet finns i minnet innan en enda byte når din destination

Varför en validerad delmängd i stället för full trohet?

HotXLS underhåller ingen andra objektmodell för XLSB. Vid öppning konverterar XlsbToOpenXml det binära paketet till ett Open XML-paket i minnet och den vanliga XML-parsern laddar det; vid sparning bygger XML-skrivaren ett paket och OpenXmlToXlsb kodar det som binära poster. Varje funktion måste därför överleva två översättningar, och en funktion bron inte kan översätta exakt avvisas i stället för att approximeras

Den stödda uppsättningen i HotXLS 2.384.113 är bred nog för de flesta dataarbetsböcker: typade värden, delade och inline-strängar, vanliga typsnitt, fyllnader, kanter och talformat, radhöjder och kolumnbredder, vanliga cellformler med cachade resultat, definierade namn, sammanslagna celler, hyperlänkar, frysta och delade rutor, AutoFilter-intervall med enkla egna kriterier, och vanliga kalkylbladstabeller. Utanför den sitter delade och array-formler, future- och användardefinierade funktioner, referenser till externa arbetsböcker, diagram och ritningar, PivotTables och slicers, VBA, villkorsstyrd formatering, datavalidering och icke-standardberäkningsinställningar som manuell beräkning eller iteration

Avvisning är obligatorisk, inte rådgivande. TXLSXWorkbook.GetConversionReport(xlsConversionExcelBinaryWorkbook) listar ostödda funktioner innan du sparar, och de posterna fallerar sparningen även med AAllowLossy = True eller en okontrollerad SaveAs. Det är medvetet. Att tyst tappa en PivotTable ur en binär kopia är den sortens förlust ingen lägger märke till förrän arbetsboken ligger framför en kund

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;   // anroparen äger rapporten
  end;
end;

Rapporten täcker den levande modellen. Slutlig paketvalidering är fortfarande den sista grinden för saker modellen aldrig ser, som felformade poster eller obekanta attribut i källfilen, så en ren rapport följt av en fallerad sparning är möjlig och sparningsresultatet är det du agerar på

Varför försvann standardtypsnittet och 0.00-formatet?

Standardtypsnittet och de inbyggda talformaten försvann eftersom XML-stil-läsaren gjorde två antaganden som håller för filer HotXLS skriver självt men inte för arbetsböcker Excel skriver. Bron bevarade båda resurserna perfekt; förlusten skedde ett lager upp, i den publika Open-vägen. Det gapet är huvudläxan från den här backend:en: en korrekt formatbro garanterar inte en trogen publik rundtur

Det första antagandet gällde fonts[0]. När HotXLS sparar XLSX skriver den ett implicit standardtypsnitt i lucka noll, och dess XML-läsare hoppar över den luckan så den användarriktade Fonts-samlingen börjar på index noll. En Excel-författad XLSB bär sitt eget explicita standardval i den luckan, och att hoppa över den kastade bort det valet. Det andra antagandet gällde talformat. Läsaren mappade bara egna numFmtId-värden (164 och uppåt) in i NumberFormats; inbyggda id:n stannade som noll. En formaterad tom cell som bar inbyggt format 2, vilket är 0.00, kom tillbaka som General

Fixen bor helt i den binära importvägen. Efter att hela paketet klarat preflighten sätter XLSB-öppningen ett privat stilläge för den synkrona XML-parsningen bara. I det läget laddar läsaren varje typsnitt inklusive lucka noll och skiftar varje fontId med ett, konverterar varje stött inbyggt id till en explicit formatkod i modellen, och använder fortfarande det ursprungliga numFmtId för datumdetektering, så ett inbyggt datumformat behåller sin datumesemantik. Ett finally-block rensar alltid läget, och en senare vanlig XLSX- eller ODS-Open rensar det bevarade standardtypsnittstillståndet också

Hur påverkar fonts[0] Excel:s kolumnbredder?

Excel beräknar kolumnbredder ur arbetsbokens standardtypsnitt i fonts[0], inte ur de typsnitt som applicerats på cellerna i den kolumnen. Kolumnens width lagras i teckenenheter av standardtypsnittets maximala sifferbredd, så att ändra fonts[0] ändrar pixelbredden på varje kolumn, inklusive kolumner vars celler alla använder ett annat typsnitt

Det producerade det mest förvirrande symptomet under hela utredningen. Den om-sparade arbetsboken hade identiska cellvärden, identiska formatkoder och identiska width-attribut, celltypsnittet och Normal-stilen pekade båda på det bevarade typsnittet, och Excel renderade ändå en datumkolumn som ####. Det skrivna fonts[0] hade återgått till Calibri 11, den maximala sifferbredden krympte, och datumen fick inte längre plats. HotXLS behåller nu källans standardtypsnitt och skriver tillbaka det som fonts[0], med fall tillbaka till Calibri 11 bara för arbetsböcker som aldrig haft en binär källa. Hela breddaritmetiken finns i Excel-kolumnbredd och max digit width (MDW) i Delphi

Språkberoende inbyggda format avvisas, inte gissas

Ett inbyggt talformat-id utan explicit formatkod accepteras bara när dess betydelse är fast; språkberoende id:n avvisas under paketpreflighten. HotXLS mappar id 0 till 22, 37 till 40 och 45 till 49 till kända koder som 0.00, 0%, mm:ss och @. De återstående id:na under 164 tillhör format vars rendering beror på Excel-installationens regionala inställningar, och importen vägrar dem med meddelandet "A locale-dependent number format requires an explicit format code"

Att hitta på en kod för de id:na vore värre än att fallera. Ett gissat östasiatiskt datummönster ser rimligt ut, går runt utan fel och visar fel sak för hälften av dina användare. Tidpunkten spelar också roll: kontrollen körs i hel-paket-preflighten, innan den befintliga arbetsboken rensas, så en avvisad fil lämnar den tidigare laddade modellen, filnamnet och källformatet orörda. Artikeln om Excel-datums serial och numFmt täcker hur inbyggda datum-id samspelar med 1900- och 1904-datumsystemen

procedure LoadBinaryWorkbook(Book: TXLSXWorkbook; const FileName: string);
var
  I: Integer;
begin
  // Binär indata kräver ivrig full laddning; selektiva lägen avvisar i förväg
  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;   // den tidigare laddade arbetsboken är fortfarande intakt
  end;

  // Formelutdata behöver en cache om inte sparningen räknar om
  Book.RecalcBeforeSave := xrmAlways;
end;

Praktiska regler för XLSB i produktionskod

Den binära backend:en är säker att lita på när du behandlar dess avvisanden som data snarare än som fel att undertrycka. Några regler följer av beteendet ovan

  • Namnge formatet vid varje binär sparning; enbart extensionen .xlsb producerar fortfarande XLSX
  • Kör GetConversionReport(xlsConversionExcelBinaryWorkbook) före sparning av användarredigerade arbetsböcker och för fram de ostödda funktionerna för användaren
  • Låt MetadataOnly vara av och SelectedSheets tom för binär indata, eftersom lazy loading skulle kräva att behålla ett konverterat arkiv
  • Ge formler ett cachat resultat eller räkna om före sparning, eftersom en saknad cache avvisas under xrmManual och under en ren xrmIfDirty
  • Budgetera minne för hela det konverterade paketet plus den binära utdatan; använd strömmande XLSX när minnet måste hållas avgränsat

När du testar, jämför mot Excel genom det publika API:et snarare än på bronivå. Standardtypsnitts- och 0.00-regressionerna klarade båda bro-testerna, och båda dök upp omedelbart i en Excel-författad arbetsbok som öppnats och sparats om genom Open och SaveAsChecked

FAQ

Detekterar HotXLS XLSB-filer automatiskt?

Ja. TXLSXWorkbook.Open inspekterar den content type som registrerats för arbetsboksdelen och validerar de binära posterna, så samma anrop laddar XLSX och XLSB, och SourceFormat rapporterar vilken som lästes

Varför skriver SaveAs med ett .xlsb-filnamn fortfarande XLSX?

HotXLS härleder aldrig utdataformatet ur extensionen. Skicka xlsxExcelBinaryWorkbook till SaveAs eller SaveAsChecked för att skriva en binär arbetsbok

Vad händer när en arbetsbok använder funktioner utanför XLSB-delmängden?

Sparningen fallerar innan destinationen ändras, även med förlustgivande sparning tillåten. GetConversionReport listar de blockrande funktionerna i förväg, och Diagnostics förklarar felet efteråt

XLSB-stöd, konverteringsrapporten och resten av arbetsboks-API:et medföljer HotXLS, den inbyggda Delphi- och C++Builder-spreadsheet-komponenten