Articolo tecnico

Celle a blocchi di riga e salvataggi XLSX in streaming in HotXLS

HotXLS memorizza le celle dei fogli di lavoro in blocchi compatti da 256 righe, risolve la formattazione di riga, colonna e rettangolo attraverso sovrapposizioni di intervallo lazy invece di creare oggetti cella, e riversa ogni riga dritta nello stream deflate del package quando salva. Insieme quei tre cambiamenti decidono il profilo di memoria di una cartella di lavoro grande: il picco di utilizzo segue la singola riga più grande anziché la dimensione del XML completo del foglio

Il motivo per cui questo conta è una forma che ogni sviluppatore di fogli di calcolo incontra prima o poi. Un utente formatta un'intera colonna — un clic, un milione di celle — e un modello a oggetti naive risponde allocando un milione di oggetti cella per tenere un indice di formato numerico. Il file su disco resta minuscolo perché il formato XLSX esprime quello come una singola voce <col>. Il processo non resta minuscolo affatto

Perché formattare una colonna costa più memoria che riempirla?

Perché la formattazione non ha dati per giustificare l'oggetto. Una cella con un valore deve esistere da qualche parte. Una cella che è vuota ma stilizzata esiste solo per portare un indice di stile, e materializzare milioni di quelle è il modo classico in cui un'applicazione Delphi per fogli di calcolo esaurisce lo spazio di indirizzamento su un file che Excel apre istantaneamente

Le sovrapposizioni di stile a intervallo rimuovono la necessità. Un'istruzione di formattazione di riga, colonna o rettangolo viene memorizzata una sola volta come intervallo più le parti di stile che contribuisce, e si risolve in modo lazy quando una cella in quell'intervallo è effettivamenteata. Le sovrapposizioni sopravvivono alle modifiche strutturali — inserire una riga dentro un blocco formattato sposta l'intervallo anziché ricostruirlo — e tornano indietro come voci compatte di colonna, riga e cella solo-stile, che è esattamente come Excel le scrive

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 è l'insieme che decide cosa contribuisce una sovrapposizione: xfpFont, xfpFill, xfpBorder, xfpNumberFormat, xfpAlignment e xfpProtection. Nominare solo le parti che intendi è ciò che permette alle sovrapposizioni di stratificarsi in modo sensato — una sovrapposizione di colonna che fornisce un formato numerico non combatte una sovrapposizione di riga che fornisce un riempimento, perché nessuna delle due rivendica la parte dell'altra

Cosa ti dà il blocco da 256 righe

Località. Le celle sono tenute in blocchi di 256 righe con handle pubblici stabili, serializzati row-major, così scrivere un foglio di lavoro percorre la memoria nell'ordine in cui emetterà byte anziché rincorrere puntatori attraverso l'heap. Gli handle stabili contano per la superficie dell'API: un handle che un chiamante conserva resta valido attraverso la riorganizzazione interna che il layout a blocchi esegue, il che è ciò che rende la rappresentazione compatta un dettaglio di implementazione anziché un cambiamento che rompe la compatibilità

La compattazione dello style pool gira alongside. Prima di ogni salvataggio, font, riempimenti, bordi, formati numerici, allineamenti e protezioni che nessuna cella referenzia vengono droppati. Le cartelle di lavoro longeve accumulano record di stile non referenziati come i documenti longevi accumulano stili non usati, e una cartella di lavoro che è stata modificata da un utente per un'ora può portarne centinaia in un file che nessuno leggerà mai

Salvataggio row-streamed, e quando non si applica

Con StreamingWrite attivo — il predefinito — ogni riga del foglio di lavoro viene scritta direttamente nello stream deflate del package. L'alternativa, che è ciò che il flag disattiva, costruisce prima il XML completo del foglio di lavoro e lo comprime dopo, così il picco di memoria scala con l'intero foglio. Lo streaming lo fa scalare con una sola riga

Le stringhe condivise e le part ausiliarie seguono la stessa disciplina attraverso un serializzatore UTF-8 riusabile che emette le voci una alla volta, limitando il picco di memoria alla singola voce più grande anziché all'intera part. Questo copre la tabella delle stringhe condivise e i record pivot, che su un foglio di lavoro analitico largo sono frequentemente più grandi di qualsiasi singolo foglio

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;

Lascialo attivo a meno che tu non abbia un motivo concreto per non farlo. Il percorso non in streaming esiste per i casi in cui qualcos'altro nella pipeline ha bisogno del XML assemblato, e pagarlo per default è pagare per un caso che la maggior parte delle applicazioni non incontra mai

Come capire se le sovrapposizioni sono davvero usate

Osserva il conteggio delle celle, non il grafico della memoria. Se un foglio di lavoro riporta un numero plausibile di celle fisiche dopo che hai applicato formattazione estesa, le sovrapposizioni fanno il loro lavoro. Se il conteggio salta della dimensione dell'intervallo formattato, qualcosa nel percorso di codice ha materializzato le celle — di solito un ciclo che legge ogni cella nell'intervallo per controllarne lo stile, che forza la risoluzione una cella alla volta e sconfigge l'intero arrangiamento

Risolvi uno stile quando ti serve il formato effettivo di una sola cella. Non risolvere uno stile per un milione di celle per scoprire che la colonna ha un formato numerico; chiedilo all'overlay. La stessa regola si applica alla scrittura: assegna valori alle celle che hanno valori, e lascia che la formattazione resti un intervallo

Dove va il resto della memoria

Una volta che celle e stili sono compatti, i consumer successivi più grandi su una cartella di lavoro grossa sono la tabella delle stringhe condivise e qualsiasi part satellite il file porti — pivot cache, disegni, XML preservato da part che il modello a oggetti non modella. Quelle hanno le proprie strategie, e la risposta onesta è che nessuna singola impostazione le risolve tutte insieme

Se il tuo collo di bottiglia è aprire anziché salvare, il caricamento selettivo è la leva: la guida sul caricamento metadata-only e selettivo dei fogli di lavoro copre la lettura di una cartella di lavoro senza pagare i fogli che non toccherai. Per il throughput sul percorso di lettura su file molto grandi, vedi le note sul parsing XLSX parallelo e il memory allocator, e per carichi di lavoro di solo output che non hanno bisogno affatto di un modello a oggetti, le scritture in streaming per job batch server sono di solito una scelta migliore di qualsiasi quantità di tuning qui

HotXLS legge e scrive XLS e XLSX da codice Delphi e C++Builder nativo senza installazione di Excel e senza automazione OLE, il che è ciò che rende queste caratteristiche di memoria osservabili e controllabili in primo luogo — la pagina del componente spreadsheet HotXLS elenca i formati supportati e le versioni di RAD Studio