Artículo técnico

Almacenamiento de celdas por bloques de filas en HotXLS

HotXLS almacena las celdas de las hojas de cálculo en bloques compactos de 256 filas, resuelve el formato de filas, columnas y rectángulos mediante superposiciones diferidas en vez de crear objetos de celda, y envía cada fila directamente al flujo deflate del paquete al guardar. Juntos esos tres cambios deciden el perfil de memoria de un libro grande: el uso pico sigue el tamaño de la fila individual más grande en vez del tamaño del XML completo de la hoja

La razón por la que esto importa tiene una forma que todo desarrollador de hojas de cálculo encuentra tarde o temprano. Un usuario formatea una columna entera — un clic, un millón de celdas — y un modelo de objetos ingenuo responde asignando un millón de objetos de celda para guardar un único índice de formato numérico. El archivo en disco se mantiene minúsculo porque el formato XLSX expresa eso como una sola entrada <col>. El proceso no se mantiene minúsculo en absoluto

¿Por qué formatear una columna cuesta más memoria que rellenarla?

Porque el formato no tiene datos que justifiquen el objeto. Una celda con un valor tiene que existir en algún lugar. Una celda que está vacía pero con estilo existe solo para cargar un índice de estilo, y materializar millones de ellas es la forma clásica en que una aplicación Delphi de hoja de cálculo se queda sin espacio de direcciones sobre un archivo que Excel abre al instante

Las superposiciones de estilos por intervalo eliminan la necesidad. Una instrucción de formato de fila, columna o rectángulo se almacena una sola vez como un rango más las partes de estilo que aporta, y se resuelve de forma diferida cuando una celda de ese rango se accede realmente. Las superposiciones sobreviven a las ediciones estructurales — insertar una fila dentro de un bloque formateado mueve el intervalo en vez de reconstruirlo — y hacen el ciclo de ida y vuelta como entradas compactas de columna, fila y celda de solo estilo, que es justamente como Excel las escribe

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 es el conjunto que decide qué aporta una superposición: xfpFont, xfpFill, xfpBorder, xfpNumberFormat, xfpAlignment y xfpProtection. Nombrar solo las partes que quieres es lo que deja que las superposiciones se apilen de forma sensata — una superposición de columna que aporta un formato numérico no pelea con una superposición de fila que aporta un relleno, porque ninguna reclama la parte de la otra

Qué te da el bloque de 256 filas

Localidad. Las celdas se guardan en bloques de 256 filas con identificadores públicos estables, serializadas por fila, así que escribir una hoja recorre la memoria en el orden en que emitirá bytes en vez de perseguir punteros por todo el montículo. Los identificadores estables importan para la superficie de la API: un identificador que quien llama sostiene sigue siendo válido a través de la reorganización interna que el diseño de bloques ejecuta, que es lo que convierte la representación compacta en un detalle de implementación en vez de un cambio rompedor

La compactación del conjunto de estilos corre junto a ello. Antes de cada guardado, se descartan las fuentes, rellenos, bordes, formatos numéricos, alineaciones y protecciones que ninguna celda referencia. Los libros de larga vida acumulan registros de estilo sin referencia igual que los documentos de larga vida acumulan estilos sin usar, y un libro que un usuario ha editado durante una hora puede llevar cientos de ellos a un archivo del que nadie los leerá jamás

Guardado por fila en flujo, y cuándo no aplica

Con StreamingWrite habilitado — el valor por defecto — cada fila de la hoja se escribe directamente en el flujo deflate del paquete. La alternativa, que es lo que la bandera apaga, construye primero el XML completo de la hoja y lo comprime después, así que la memoria paca escala con la hoja entera. El flujo hace que escale con una fila

Las cadenas compartidas y las partes auxiliares siguen la misma disciplina a través de un serializador UTF-8 reutilizable que emite entradas una a la vez, acotando la memoria paca por la entrada individual más grande en vez de por la parte entera. Eso cubre la tabla de cadenas compartidas y los registros de tabla dinámica, que en una hoja analítica amplia con frecuencia son más grandes que cualquier hoja de cálculo individual

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;

Déjalo activado salvo que tengas una razón concreta para no hacerlo. La ruta sin flujo existe para los casos en que otra cosa en el flujo necesita el XML ensamblado, y pagar por ello por defecto es pagar por un caso que la mayoría de las aplicaciones nunca toca

Cómo saber si las superposiciones se están usando de verdad

Mira el conteo de celdas, no la gráfica de memoria. Si una hoja reporta una cantidad plausible de celdas físicas después de aplicar formato amplio, las superposiciones están haciendo su trabajo. Si el conteo salta al tamaño del rango formateado, algo en la ruta del código materializó las celdas — por lo general un bucle que lee cada celda del rango para revisar su estilo, lo cual fuerza la resolución una celda a la vez y derrota todo el arreglo

Resuelve un estilo cuando necesitas el formato efectivo de una celda. No resuelvas un estilo para un millón de celdas para averiguar que la columna tiene un formato numérico; pregúntale a la superposición. La misma regla aplica al escribir: asigna valores a las celdas que tienen valores, y deja que el formato se quede como un intervalo

A dónde se va la memoria restante

Una vez que las celdas y los estilos son compactos, los siguientes consumidores más grandes en un libro grande son la tabla de cadenas compartidas y las partes satélite que el archivo lleve — cachés de tabla dinámica, dibujos, XML preservado de partes que el modelo de objetos no modela. Esas tienen sus propias estrategias, y la respuesta honesta es que ningún ajuste individual las resuelve todas a la vez

Si tu cuello de botella es abrir en vez de guardar, la carga selectiva es la palanca: el recorrido por la carga de hojas de cálculo solo con metadatos y selectiva cubre cómo leer un libro sin pagar por las hojas que no vas a tocar. Para el rendimiento de la ruta de lectura en archivos muy grandes, consulta las notas sobre el análisis paralelo de XLSX y el asignador de memoria, y para cargas de solo salida que nunca necesitan un modelo de objetos, los flujos de escritura para trabajos por lotes del servidor suelen encajar mejor que cualquier ajuste aquí

HotXLS lee y escribe XLS y XLSX desde código nativo de Delphi y C++Builder sin instalación de Excel y sin automatización OLE, que es justamente lo que vuelve observables y controlables estas características de memoria en primer lugar — la página del componente de hoja de cálculo HotXLS lista los formatos soportados y las versiones de RAD Studio