A pergunta "essa pasta de trabalho ainda define um nome chamado TaxRate?" é respondida por uma pequena parte de um pacote XLSX. Nomes de planilhas, o estado hidden ou very-hidden delas e todo nome definido no nível da pasta vivem no xl/workbook.xml (ECMA-376 Parte 1, §18.2), geralmente alguns kilobytes. As células vivem em outro lugar, uma parte xl/worksheets/sheetN.xml por planilha mais uma tabela de shared strings, e é aí que estão quase todos os bytes. Um carregador que insiste em construir a pasta de trabalho completa antes de você poder perguntar qualquer coisa vai inflar e analisar tudo isso, para cada arquivo da pasta, para responder a uma pergunta que os primeiros kilobytes já resolveram
A divisão que importa é entre ler a forma de uma pasta de trabalho e ler o seu peso. Um carregador que consegue parar depois da forma, carregar o peso para um subconjunto escolhido de planilhas e buscar mais uma planilha depois transforma "abrir uma pasta de trabalho" de um passo tudo-ou-nada em algo que você delimita por chamada. Ele também traz duas armadilhas, uma sobre bases de índice e outra sobre quanto tempo o arquivo de origem precisa ficar parado
O que um Open somente metadados lê, e onde ele para
Com o TXLSXWorkbook.MetadataOnly definido como True antes do Open, o HotXLS analisa a parte da pasta de trabalho, estilos, tema, nomes definidos, nomes e estados de visibilidade de planilhas, conexões, links externos e as propriedades do documento de docProps/core.xml e docProps/app.xml. Depois pula o XML de células de toda planilha e as partes satélites por planilha: comentários, drawings e tabelas. A tabela de shared strings também é pulada, o que importa mais do que parece, porque em pastas de trabalho cheias de texto essa única parte pode rivalizar com as planilhas em tamanho
A pasta de trabalho que volta é estruturalmente completa. O Sheets.Count está certo, cada planilha tem o nome real e a Visibility dela, o DefinedNames está populado, o ExternalLinks lista todo alvo ligado, e Title, Author e LastModifiedBy carregam o que o arquivo declarou. Toda planilha simplesmente tem zero células
Esses são exatamente os dados de que uma auditoria precisa. Quais pastas de trabalho escondem uma planilha? Quais ainda definem TaxRate, e apontando para o quê? Quais ligam para um file share aposentado no ano passado? O scanner abaixo responde as três para um arquivo. Note as duas convenções de indexação lado a lado: Sheets[] é 1-based, acompanhando o engine XLS, enquanto DefinedNames e ExternalLinks são listas comuns 0-based
procedure ScanWorkbook(const FileName: WideString; Report: TStrings);
var
Workbook: TXLSXWorkbook;
Name: TXLSXDefinedName;
I: Integer;
begin
Workbook := TXLSXWorkbook.Create;
try
Workbook.MetadataOnly := True; // lido pelo Open, então defina primeiro
if Workbook.Open(FileName) <> 1 then
begin
Report.Add(FileName + ': open failed');
Exit;
end;
for I := 1 to Workbook.Sheets.Count do // Sheets[] é 1-based
case Workbook.Sheets[I].Visibility of
xlsxSheetHidden:
Report.Add(FileName + ': hidden sheet ' + Workbook.Sheets[I].Name);
xlsxSheetVeryHidden:
Report.Add(FileName + ': very hidden sheet ' + Workbook.Sheets[I].Name);
end;
Name := Workbook.DefinedNames.FindByName('TaxRate');
if Name <> nil then
Report.Add(FileName + ': TaxRate = ' + Name.Formula);
for I := 0 to Workbook.ExternalLinks.Count - 1 do // lista 0-based
Report.Add(FileName + ': links to ' + Workbook.ExternalLinks[I].Target);
finally
Workbook.Free;
end;
end;
O scanner lê a Visibility em vez do Boolean Visible de propósito. O Visible é False para os dois estados ocultos, e uma planilha very hidden é justamente a que uma auditoria mais quer marcar, já que o diálogo Unhide do Excel não a lista e só VBA ou uma edição direta do arquivo a traz de volta
Carregando duas planilhas de quarenta com SelectedSheets
O SelectedSheets é um TStringList de propriedade da pasta de trabalho. Quando ele guarda pelo menos um nome, o Open analisa o XML de células apenas para as planilhas que ele nomeia; toda outra planilha é criada com o nome e a visibilidade corretos e nenhuma célula. Uma lista vazia, o padrão, carrega todas as planilhas. Os nomes são procurados com o IndexOf da própria lista, então com configurações padrão a correspondência ignora maiúsculas, o que acompanha a regra do Excel de que dois nomes de planilha em uma pasta de trabalho não podem diferir só por maiúsculas
Workbook := TXLSXWorkbook.Create;
Workbook.SelectedSheets.Add('Summary');
Workbook.SelectedSheets.Add('Q3 Detail');
if Workbook.Open('consolidated-2026.xlsx') = 1 then
ShowSheet(Workbook.Sheets[1]); // ShowSheet is your own UI code
Os dois interruptores interagem em uma única direção. MetadataOnly = True sobrepõe a seleção e pula todas as planilhas, então uma pasta configurada com ambos volta sem célula nenhuma, e a seleção parece quebrada quando foi simplesmente sobrepujada. Use um por Open: somente metadados para descoberta, uma seleção para trabalho direcionado
O carregamento seletivo também passa pela fase de parse paralelo quando o ParallelParse está ligado; planilhas não selecionadas são puladas dentro dos jobs dos workers, então as threads gastam o tempo delas nas planilhas que você pediu. As mesmas duas propriedades se aplicam quando a origem é uma pasta binária .xlsb, com uma regra de escopo: um .xlsb que carrega um projeto VBA tem de ser aberto sem essas duas opções; com qualquer uma delas definida, o Open reporta a leitura seletiva como não suportada
Por que LoadSheet(1) carrega a planilha errada?
Porque o LoadSheet recebe uma posição 0-based enquanto o Sheets[] é 1-based. O LoadSheet(1) materializa a segunda planilha da pasta de trabalho, e como carregar uma planilha que já tem células é um no-op que retorna True, um índice embaralhado pode parecer sucesso enquanto a planilha que o usuário clicou continua vazia. A conversão pertence a exatamente um lugar no seu código
A segunda armadilha é o tempo de vida do arquivo. O LoadSheet não segura o arquivo aberto entre chamadas. Ele reabre o arquivo original pelo nome, analisa a parte da pasta de trabalho, os estilos e as shared strings, e então analisa a planilha pedida. Antes de confiar naquele arquivo, o HotXLS compara o seu tamanho e horário da última escrita com os valores capturados no Open; se outro processo salvou por cima da pasta de trabalho enquanto isso, o LoadSheet retorna False em vez de misturar células de uma versão diferente no modelo que você já segura
procedure TReportForm.ShowSheetNo(SheetNo: Integer); // SheetNo é 1-based, como Sheets[]
var
Sheet: TXLSXWorksheet;
begin
Sheet := FWorkbook.Sheets[SheetNo];
if Sheet.Cells.Count = 0 then
if not FWorkbook.LoadSheet(SheetNo - 1) then // LoadSheet quer a posição 0-based
begin
StatusBar.SimpleText := 'The workbook changed on disk; reopen it';
Exit;
end;
Grid.ShowSheet(Sheet); // código de grid seu
end;
Algumas consequências se seguem desse desenho. Uma pasta de trabalho aberta de um TStream não tem arquivo de origem para onde voltar, então o LoadSheet retorna False para um .xlsx aberto de stream; o mesmo vale para uma pasta de trabalho criptografada, que é decifrada em memória antes da análise. Um .xlsb binário aberto seletivamente de um stream é a exceção, porque o HotXLS estagia uma cópia temporária do stream para as planilhas diferidas. E uma planilha genuinamente vazia será re-lida a cada chamada, já que zero células é também a aparência de uma planilha pulada
O modelo de custo decide como usá-lo. Toda chamada LoadSheet re-analisa a tabela de shared strings, então materializar dez planilhas uma por uma paga essa parte dez vezes. Para um visualizador em que o usuário abre mais uma aba de vez em quando, é um preço aceitável. Quando você já conhece o conjunto de planilhas, reabra com SelectedSheets em vez disso: uma passada de arquivo, um parse de shared strings. Há uma diferença de conteúdo também: o LoadSheet traz as células, valores, fórmulas e formatos de uma planilha, enquanto comentários, drawings e tabelas chegam com um Open, então planilhas cujos comentários ou tabelas o seu código lê pertencem à seleção desde o começo
Por que uma ida e volta muda partes que você nunca tocou?
Um save que regenera tudo reescreve bytes que nunca mudaram. Descomprima o tema, construa um modelo de objetos, serialize-o, comprima de novo, e o resultado difere do original mesmo que ninguém tenha editado o tema. Para projetos VBA e pivot caches o custo é maior do que um diff barulhento: um projeto VBA é um compound file binário com streams internos próprios, e um pivot cache guarda um snapshot dos dados de origem cujos campos as definições de pivot table endereçam por posição, então a coisa mais segura a fazer com qualquer um deles, quando ninguém mudou, é deixar os bytes em paz
O PreserveRawParts, que assume True, faz o SaveAs copiar os bytes comprimidos de partes inalteradas direto do arquivo de origem. Isso cobre o tema, o projeto VBA, conexões, charts, mídia incorporada, configurações de impressora, pivot tables e definições e registros de pivot cache, cada um apenas enquanto o modelo diz que ele não foi modificado. Essas partes nunca são infladas ou re-comprimidas no caminho. Definir a propriedade como False força re-serialização completa de toda parte, que é o interruptor para alcançar quando você quer que a saída reflita apenas o que o modelo de objetos contém
O caminho de cópia bruta usa a mesma checagem de arquivo que o LoadSheet: a origem ainda precisa existir com o tamanho e o timestamp vistos no Open. Quando não, o SaveAs serializa essas partes normalmente e o arquivo continua completo. Dois casos sempre tomam o caminho de serializar: uma pasta de trabalho cuja origem era um .xlsb binário, que é convertido para XML na leitura, e um save com StrictOOXML definido, onde os namespaces das partes mudam
Salvar uma pasta de trabalho parcialmente carregada pede uma decisão. Para uma origem .xlsb, o SaveAs recusa enquanto qualquer planilha ainda estiver diferida e registra "Load every deferred binary worksheet before saving" no Diagnostics. Para um .xlsx, trate uma pasta de trabalho carregada seletivamente como uma visão de leitura: uma planilha stub não tem células para o gerador emitir, então ou chame o LoadSheet para cada stub primeiro ou reabra sem seleção antes de editar e salvar
Um pipeline de descoberta que reabre de propósito
O padrão que se sustenta tem duas passadas. A passada um abre todo arquivo somente metadados e decide. A passada dois reabre apenas os arquivos que casaram, com uma seleção de planilhas, e faz o trabalho. Reabrir parece desperdício até você contar o que a passada um evitou: o segundo Open é o único que analisa células, e analisa apenas as planilhas que a passada um escolheu
procedure ProcessInvoiceFile(const FileName: WideString);
var
Probe, Workbook: TXLSXWorkbook;
Wanted: TStringList;
I: Integer;
begin
Wanted := TStringList.Create;
try
// Passada 1: descoberta, sem células
Probe := TXLSXWorkbook.Create;
try
Probe.MetadataOnly := True;
if Probe.Open(FileName) <> 1 then
Exit;
for I := 1 to Probe.Sheets.Count do
if Pos('Invoice', Probe.Sheets[I].Name) > 0 then
Wanted.Add(Probe.Sheets[I].Name);
finally
Probe.Free;
end;
if Wanted.Count = 0 then
Exit; // nada neste arquivo vale análise
// Passada 2: células só para as planilhas escolhidas
Workbook := TXLSXWorkbook.Create;
try
Workbook.SelectedSheets.Assign(Wanted);
if Workbook.Open(FileName) = 1 then
PostInvoices(Workbook); // código de processamento seu
finally
Workbook.Free;
end;
finally
Wanted.Free;
end;
end;
Limpar o MetadataOnly depois do Open não transforma a sonda em uma pasta completa. A flag é lida ao abrir; mudá-la depois só afeta o próximo Open daquele objeto. A sonda ainda pode crescer uma planilha por vez via LoadSheet, e para uma ou duas planilhas é razoável, mas um pipeline é melhor servido pela reabertura limpa, que também traz os comentários e tabelas de cada planilha junto
Onde isso se encaixa com as outras alavancas de arquivo grande
O carregamento seletivo corta o que você analisa. Ele não faz nada pelo custo de uma planilha depois de carregada, e não ajuda quando o job precisa de toda célula de toda planilha. Esse caso é sobre armazenamento, coberto no artigo sobre armazenamento de células em blocos de linhas e estilos por intervalo, e sobre throughput de parse, coberto em parsing paralelo de XLSX e o alocador de memória. Quando a tarefa é uma leitura pura sem edição nenhuma, o leitor direto em streaming pula o modelo de objetos da pasta de trabalho inteiro e é a ferramenta melhor do que qualquer estratégia de carga que construa um
Tudo isso roda dentro do seu próprio executável Delphi ou C++Builder, sem instalação do Excel no servidor que faz a varredura. Formatos suportados, edições e licenciamento estão listados na página do componente de planilhas Delphi da HotXLS