Artigo Técnico

Células em Blocos de Linha e Gravações XLSX em Fluxo no HotXLS

O HotXLS armazena células de folha de cálculo em blocos compactos de 256 linhas, resolve formatação de linhas, colunas e retângulos através de sobreposições de intervalo preguiçosas em vez de criar objetos de célula, e faz o fluxo de cada linha diretamente para o stream deflate do pacote ao gravar. Juntas essas três mudanças decidem o perfil de memória de um livro grande: o pico de uso segue a maior linha individual em vez do tamanho do XML completo da folha de cálculo

A razão pela qual isto importa é uma forma que todos os programadores de folhas de cálculo encontram eventualmente. Um utilizador formata uma coluna inteira — um clique, um milhão de células — e um modelo de objetos ingénuo responde alocando um milhão de objetos de célula para conter um único índice de formato numérico. O ficheiro em disco mantém-se minúsculo porque o formato XLSX expressa isso como uma única entrada <col>. O processo não se mantém minúsculo de todo

Porque razão é que formatar uma coluna custa mais memória do que preenchê-la?

Porque a formatação não tem dados para justificar o objeto. Uma célula com um valor tem de existir em algum lado. Uma célula que está vazia mas estilizada existe apenas para transportar um índice de estilo, e materializar milhões dessas é a forma clássica como uma aplicação de folha de cálculo Delphi fica sem espaço de endereçamento num ficheiro que o Excel abre instantaneamente

As sobreposições de estilo por intervalo removem essa necessidade. Uma instrução de formatação de linha, coluna ou retângulo é armazenada uma única vez como um intervalo mais as partes de estilo que contribui, e resolve-se preguiçosamente quando uma célula nesse intervalo é efetivamente acedida. As sobreposições sobrevivem a edições estruturais — inserir uma linha dentro de um bloco formatado move o intervalo em vez de o reconstruir — e fazem a ida e volta como entradas compactas de coluna, linha e célula só com estilo, que é exatamente como o Excel as escreve

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 é o conjunto que decide o que uma sobreposição contribui: xfpFont, xfpFill, xfpBorder, xfpNumberFormat, xfpAlignment e xfpProtection. Nomear apenas as partes que pretende é o que permite às sobreposições sobrepor-se de forma sensata — uma sobreposição de coluna que fornece um formato numérico não compete com uma sobreposição de linha que fornece um preenchimento, porque nenhuma reclama a parte da outra

O que o bloco de 256 linhas lhe dá

Localidade. As células são mantidas em blocos de 256 linhas com manípulos públicos estáveis, serializados linha-principal, pelo que escrever uma folha de cálculo percorre a memória pela ordem em que vai emitir bytes em vez de perseguir ponteiros pela heap. Os manípulos estáveis importam para a superfície da API: um manípulo que um chamador detém mantém-se válido através da reorganização interna que a disposição em blocos realiza, que é o que torna a representação compacta um detalhe de implementação em vez de uma mudança de rutura

A compactação do conjunto de estilos corre ao lado dela. Antes de cada gravação, tipos de letra, preenchimentos, margens, formatos numéricos, alinhamentos e proteções que nenhuma célula referencia são descartados. Os livros antigos acumulam registos de estilo sem referência da mesma forma que os documentos antigos acumulam estilos não usados, e um livro que foi editado por um utilizador durante uma hora pode transportar centenas deles num ficheiro de onde ninguém os vai ler

Gravação em fluxo por linha, e quando não se aplica

Com StreamingWrite ativado — a predefinição — cada linha de folha de cálculo é escrita diretamente no stream deflate do pacote. A alternativa, que é o que o sinalizador desliga, constrói o XML completo da folha de cálculo primeiro e comprime-o depois, pelo que a memória de pico escala com a folha inteira. O fluxo faz com que escale com uma linha

As cadeias partilhadas e as partes auxiliares seguem a mesma disciplina através de um serializador UTF-8 reutilizável que emite entradas uma de cada vez, limitando a memória de pico pela maior entrada individual em vez de pela parte inteira. Isso cobre a tabela de cadeias partilhadas e os registos pivô, que num livro analítico largo são frequentemente maiores do que qualquer folha 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;

Deixe-o ligado a menos que tenha uma razão concreta para o contrário. O caminho não em fluxo existe para os casos em que outra coisa no pipeline precisa do XML montado, e pagá-lo por predefinição é pagar por um caso que a maioria das aplicações nunca atinge

Como dizer se as sobreposições estão efetivamente a ser usadas

Veja a contagem de células, não o gráfico de memória. Se uma folha de cálculo reporta um número plausível de células físicas depois de ter aplicado formatação ampla, as sobreposições estão a fazer o seu trabalho. Se a contagem salta pelo tamanho do intervalo formatado, algo no caminho de código materializou as células — normalmente um ciclo que lê cada célula no intervalo para verificar o seu estilo, o que força a resolução uma célula de cada vez e derrota todo o arranjo

Resolva um estilo quando precisa do formato efetivo de uma célula. Não resolva um estilo para um milhão de células para descobrir que a coluna tem um formato numérico; pergunte à sobreposição. A mesma regra se aplica à escrita: atribua valores às células que têm valores, e deixe a formatação ficar um intervalo

Para onde vai a memória restante

Depois de células e estilos compactos, os maiores consumidores seguintes num livro grande são a tabela de cadeias partilhadas e quaisquer partes satélite que o ficheiro transporte — caches pivô, desenhos, XML preservado de partes que o modelo de objetos não modela. Essas têm as suas próprias estratégias, e a resposta honesta é que nenhuma definição isolada resolve todas de uma vez

Se o seu gargalo é abrir em vez de gravar, o carregamento seletivo é a alavanca: o guia de carregamento de folhas de cálculo apenas com metadados e seletivo aborda ler um livro sem pagar pelas folhas que não vai tocar. Para débito do caminho de leitura em ficheiros muito grandes, veja as notas sobre análise XLSX paralela e o alocador de memória, e para cargas de trabalho só de saída que nunca precisam de um modelo de objetos de todo, escritas em fluxo para trabalhos em lote de servidor é normalmente um encaixe melhor do que qualquer afinação aqui

O HotXLS lê e escreve XLS e XLSX a partir de código nativo Delphi e C++Builder sem qualquer instalação de Excel e sem automatização OLE, que é o que torna estas características de memória observáveis e controláveis em primeiro lugar — a página do componente de folha de cálculo HotXLS lista os formatos suportados e versões de RAD Studio