Teknisk artikel

Radblockscellagring och strömmande XLSX-sparande i HotXLS

HotXLS lagrar kalkylbladsceller i kompakta 256-radsblock, löser rad-, kolumn- och rektangel-formatering genom lat intervallöverlägg i stället för att skapa cellobjekt, och strömmar varje rad rakt in i paketets deflate-ström vid sparande. Tillsammans bestämmer de tre ändringarna minnesprofilen för en stor arbetsbok: spetsanvändning följer den största enskilda raden i stället för storleken på hela kalkylbladets XML

Anledningen till att detta betyder något är en form som varje kalkylbladsutvecklare möter så småningom. En användare formaterar en hel kolumn — ett klick, en miljon celler — och en naiv objektmodell svarar med att allokera en miljon cellobjekt för att hålla ett nummerformatindex. Filen på disk förblir liten eftersom XLSX-formatet uttrycker det som en enda <col>-post. Processen förblir inte liten alls

Varför kostar formatering av en kolumn mer minne än att fylla den?

Eftersom formatering inte har någon data att motivera objektet. En cell med ett värde måste existera någonstans. En cell som är tom men formaterad existerar endast för att bära ett stilindex, och att materialisera miljoner av dem är det klassiska sättet på vilket en Delphi-kalkylbladsapplikation får slut på adressutrymme på en fil som Excel öppnar omedelbart

Intervallstilsöverlägg tar bort behovet. En rad-, kolumn- eller rektangelformateringsinstruktion lagras en gång som ett intervall plus de stildelar den bidrar med, och löser sig lat när en cell i det intervallet faktiskt nås. Överlägg överlever strukturella redigeringar — att infoga en rad inuti ett formaterat block flyttar intervallet i stället för att bygga om det — och de rundturssparas som kompakta kolumn-, rad- och stilbara-cellposter, vilket är exakt hur Excel skriver dem

var
  Sheet: TXLSXWorksheet;
  State: TXLSXCellStyleState;
begin
  Sheet := Workbook.Sheets[1];
  // Style indexes come from the workbook style pools, e.g. from a cell
  // you have already formatted the way you want the range to look
  State := BuildStateFromTemplateCell(Sheet.Cells.Item[1, 1]);
  // Format columns B..D without creating a single empty cell object
  Sheet.StyleOverlays.Add(1, 2, MaxRowIndex, 4,
    [xfpNumberFormat, xfpAlignment], State);
end;

TXLSXFormatParts är den mängd som beslutar vad ett överlägg bidrar med: xfpFont, xfpFill, xfpBorder, xfpNumberFormat, xfpAlignment och xfpProtection. Att namnge endast de delar du avser är vad som låter överlägg skikta sig förståndigt — ett kolumnöverlägg som tillhandahåller ett nummerformat slåss inte med ett radöverlägg som tillhandahåller en fyllning, eftersom ingen av dem gör anspråk på den andras del

Vad 256-radsblocket ger dig

Lokalitet. Cellerna hålls i block om 256 rader med stabila publika referenser, serialiserade rad-major, så att skriva ett kalkylblad vandrar minnet i den ordning det kommer emittera byte i stället för att jaga pekare över heapen. Stabila referenser betyder något för API-ytan: en referens en anropare håller förblir giltig över den interna omorganisation blocklayouten utför, vilket är vad som gör den kompakta representationen till en implementeringsdetalj i stället för en brytande ändring

Stilpoolskompaktkörning löper bredvid den. Innan varje sparande släpps teckensnitt, fyllningar, ramar, nummerformat, justeringar och skydd som ingen cell refererar. Långlivade arbetsböcker ackumulerar orefererade stilposter så som långlivade dokument ackumulerar oanvända stilar, och en arbetsbok som redigerats av en användare i en timme kan bära hundratals av dem in i en fil ingen någonsin kommer läsa dem från

Radströmmat sparande och när det inte gäller

Med StreamingWrite aktiverat — standard — skrivs varje kalkylbladsrad direkt in i paketets deflate-ström. Alternativet, vilket är vad flaggan stänger av, bygger hela kalkylbladets XML först och komprimerar den i efterhand, så att spetsminnet skalar med hela bladet. Strömmande får det att skala med en rad

Delade strängar och hjälpdelar följer samma disciplin genom en återanvändbar UTF-8-serialiserare som emitterar poster en i taget, vilket begränsar spetsminnet av den största enskilda posten i stället för hela delen. Det täcker tabellen för delade strängar och pivottabellsposter, vilka på en bred analytisk arbetsbok ofta är större än något enskilt kalkylblad

var
  Workbook: TXLSXWorkbook;
begin
  Workbook := TXLSXWorkbook.Create(nil);
  try
    Workbook.Open('ledger-2026.xlsx');
    // StreamingWrite defaults to True; turn it off only when a downstream
    // step requires the whole worksheet XML to exist before compression
    Workbook.StreamingWrite := True;
    Workbook.SaveAs('ledger-2026-out.xlsx');
  finally
    Workbook.Free;
  end;
end;

Lämna den på såvida du inte har en konkret anledning att inte göra det. Den icke-strömmande vägen existerar för de fall där någonting annat i pipelinen behöver den sammansatta XML:en, och att betala för den som standard är att betala för ett fall de flesta applikationer aldrig träffar

Hur du vet huruvida överlägg faktiskt används

Titta på cellantalet, inte minnesgrafen. Om ett kalkylblad rapporterar ett rimligt antal fysiska celler efter att du har applicerat bred formatering gör överläggen sitt jobb. Om antalet hoppar med storleken på det formaterade intervallet har någonting i kodvägen materialiserat cellerna — vanligtvis en loop som läser varje cell i intervallet för att kontrollera dess stil, vilket tvingar upplösning en cell i taget och besegrar hela arrangemanget

Lös upp en stil när du behöver en cells effektiva format. Lös inte upp en stil för en miljon celler för att få reda på att kolumnen har ett nummerformat; fråga överlägget. Samma regel gäller vid skrivande: tilldela värden till de celler som har värden, och låt formatering förbli ett intervall

Var det återstående minnet går

När celler och stilar väl är kompakta är de näst största konsumenterna på en stor arbetsbok tabellen för delade strängar och vilka satellitdelar filen än bär — pivotcachar, ritningar, bevarad XML från delar objektmodellen inte modellerar. De har sina egna strategier, och det ärliga svaret är att ingen enskild inställning löser samtliga på en gång

Om din flaskhals är öppnande i stället för sparande är selektiv inläsning spaken: genomgången av metadatabegränsad och selektiv kalkylbladsinläsning täcker att läsa en arbetsbok utan att betala för de blad du inte kommer röra. För läsvägs-genomströmning på mycket stora filer, se noterna om parallell XLSX-tolkning och minnesallokatorn, och för utdataendast-arbetsbelastningar som aldrig behöver en objektmodell alls är strömmande skrivning för serverbatchjobb oftast en bättre passform än någon mängd trimning här

HotXLS läser och skriver XLS och XLSX från inbyggd Delphi- och C++Builder-kod utan Excel-installation och utan OLE-automatisering, vilket är vad som gör dessa minnesegenskaper observerbara och kontrollerbara från första början — HotXLS kalkylbladskomponentsida listar de format och RAD Studio-versioner som stöds