La pregunta «este libro, ¿todavía define un nombre llamado TaxRate?» la responde una parte pequeña de un paquete XLSX. Los nombres de hojas, su estado oculto o muy oculto y cada nombre definido a nivel libro viven en xl/workbook.xml (ECMA-376 Part 1, §18.2), usualmente unos pocos kilobytes. Las celdas viven en otro lado, una parte xl/worksheets/sheetN.xml por hoja más una tabla de shared strings, y ahí es donde están casi todos los bytes. Un cargador que insiste en construir el libro completo antes de que usted pueda preguntar nada inflará y parseará todo eso, por cada archivo de la carpeta, para responder una pregunta que los primeros pocos kilobytes ya resolvieron
La división que importa es entre leer la forma de un libro y leer su peso. Un cargador que puede detenerse después de la forma, cargar el peso para un subconjunto elegido de hojas, y traer una hoja más después convierte «abrir un libro» de un paso todo-o-nada en algo que usted acota por llamada. También trae consigo dos trampas, una sobre las bases de índices y otra sobre cuánto tiempo tiene que quedarse quieto el archivo de origen
Qué lee un Open solo con metadatos, y dónde se detiene
Con TXLSXWorkbook.MetadataOnly en True antes de Open, HotXLS parsea la parte del libro, los estilos, el theme, los nombres definidos, los nombres de hojas y estados de visibilidad, las conexiones, los vínculos externos y las propiedades del documento desde docProps/core.xml y docProps/app.xml. Luego se salta el XML de celdas de cada hoja y las partes satélite por hoja: comentarios, dibujos y tablas. La tabla de shared strings también se salta, lo que importa más de lo que suena, porque en libros cargados de texto esa única parte puede rivalizar con las hojas en tamaño
El libro que vuelve está estructuralmente completo. Sheets.Count es correcto, cada hoja tiene su nombre real y Visibility, DefinedNames está poblado, ExternalLinks lista cada objetivo vinculado, y Title, Author y LastModifiedBy cargan lo que el archivo declaró. Cada hoja simplemente tiene cero celdas
Eso es exactamente el dato sobre el que corre una auditoría. ¿Qué libros esconden una hoja? ¿Cuáles todavía definen TaxRate, y hacia qué? ¿Cuáles vinculan a un recurso compartido de archivos que se retiró el año pasado? El escáner de abajo responde las tres para un archivo. Note las dos convenciones de indexado lado a lado: Sheets[] es 1-based, coincidiendo con el motor XLS, mientras que DefinedNames y ExternalLinks son listas ordinarias 0-based
procedure ScanWorkbook(const FileName: WideString; Report: TStrings);
var
Workbook: TXLSXWorkbook;
Name: TXLSXDefinedName;
I: Integer;
begin
Workbook := TXLSXWorkbook.Create;
try
Workbook.MetadataOnly := True; // lo lee Open, así que fíjelo primero
if Workbook.Open(FileName) <> 1 then
begin
Report.Add(FileName + ': open failed');
Exit;
end;
for I := 1 to Workbook.Sheets.Count do // Sheets[] es 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;
El escáner lee Visibility en lugar del Boolean Visible a propósito. Visible es False para ambos estados ocultos, y una hoja muy oculta es la que una auditoría más quiere marcar, ya que el diálogo Unhide de Excel no la lista y solo VBA o una edición directa del archivo la trae de vuelta
Cargar dos hojas de entre cuarenta con SelectedSheets
SelectedSheets es una TStringList propiedad del libro. Cuando sostiene al menos un nombre, Open parsea el XML de celdas solo para las hojas que nombra; cada otra hoja se crea con su nombre y visibilidad correctos y sin celdas. Una lista vacía, el valor por defecto, carga todas las hojas. Los nombres se buscan con el IndexOf propio de la lista, así que con los ajustes por defecto la coincidencia ignora mayúsculas, lo que se alinea con la regla de Excel de que dos nombres de hoja en un libro no pueden diferir solo en mayú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 es su propio código de UI
Los dos interruptores interactúan en una sola dirección. MetadataOnly = True se impone a la selección y se salta todas las hojas, así que un libro configurado con ambos vuelve sin celdas en ninguna parte, y la selección se ve rota cuando simplemente fue sobrepasada. Use uno por Open: solo-metadatos para descubrimiento, una selección para trabajo dirigido
La carga selectiva también corre por la fase de parseo paralelo cuando ParallelParse está activo; las hojas no seleccionadas se saltan dentro de los trabajos de los workers, así que los hilos gastan su tiempo en las hojas que usted pidió. Las mismas dos propiedades aplican cuando el origen es un libro binario .xlsb, con una regla de alcance: un .xlsb que carga un proyecto VBA tiene que abrirse sin estas dos opciones; con cualquiera de las dos puestas, Open reporta la lectura selectiva como no soportada
¿Por qué LoadSheet(1) carga la hoja equivocada?
Porque LoadSheet toma una posición 0-based mientras Sheets[] es 1-based. LoadSheet(1) materializa la segunda hoja del libro, y como cargar una hoja que ya tiene celdas es un no-op que devuelve True, un índice revuelto puede verse como éxito mientras la hoja en que el usuario clicó se queda vacía. La conversión pertenece a exactamente un lugar de su código
La segunda trampa es la vida del archivo. LoadSheet no sostiene el archivo abierto entre llamadas. Reabre el archivo original por nombre, parsea la parte del libro, los estilos y las shared strings, y luego parsea la hoja pedida. Antes de confiar en ese archivo, HotXLS compara su tamaño y su hora de última escritura con los valores capturados en Open; si otro proceso guardó sobre el libro mientras tanto, LoadSheet devuelve False en lugar de mezclar celdas de una versión distinta en el modelo que usted ya sostiene
procedure TReportForm.ShowSheetNo(SheetNo: Integer); // SheetNo es 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 quiere la posición 0-based
begin
StatusBar.SimpleText := 'The workbook changed on disk; reopen it';
Exit;
end;
Grid.ShowSheet(Sheet); // su propio código de grid
end;
Unas consecuencias se siguen de ese diseño. Un libro abierto desde un TStream no tiene archivo de origen al cual volver, así que LoadSheet devuelve False para un .xlsx abierto por stream; lo mismo vale para un libro cifrado, que se descifra a memoria antes de parsear. Un .xlsb binario abierto selectivamente desde un stream es la excepción, porque HotXLS prepara una copia temporal del stream para las hojas diferidas. Y una hoja genuinamente vacía se re-leerá en cada llamada, ya que cero celdas es también lo que parece una hoja saltada
El modelo de costos decide cómo usarlo. Cada llamada a LoadSheet vuelve a parsear la tabla de shared strings, así que materializar diez hojas una por una paga esa parte diez veces. Para un visor donde el usuario abre una pestaña más de vez en cuando, es un precio fino. Cuando usted ya conoce el conjunto de hojas, reabra con SelectedSheets en cambio: una pasada del archivo, un parseo de shared strings. Hay también una diferencia de contenido: LoadSheet trae las celdas, valores, fórmulas y formatos de una hoja, mientras que los comentarios, dibujos y tablas llegan con un Open, así que las hojas cuyos comentarios o tablas lee su código pertenecen a la selección desde el principio
¿Por qué un round trip cambia partes que usted jamás tocó?
Un guardado que regenera todo reescribe bytes que jamás cambiaron. Descomprima el theme, construya un modelo de objetos, serialícelo, comprímalo otra vez, y el resultado difiere del original aunque nadie editó el theme. Para proyectos VBA y pivot caches el costo es mayor que un diff ruidoso: un proyecto VBA es un archivo compuesto binario con sus propios streams internos, y un pivot cache sostiene una instantánea de datos de origen cuyos campos las definiciones de la pivot table direccionan por posición, así que lo más seguro que se puede hacer con cualquiera de los dos, cuando nadie lo cambió, es dejar sus bytes en paz
PreserveRawParts, que vale True por defecto, hace que SaveAs copie los bytes comprimidos de las partes sin cambios directo desde el archivo de origen. Eso cubre el theme, el proyecto VBA, las conexiones, los charts, los medios incrustados, los ajustes de impresora, las pivot tables y las definiciones y registros de pivot cache, cada uno solo mientras el modelo dice que no fue modificado. Esas partes jamás se inflan o se vuelven a desinflar en el camino. Poner la propiedad en False fuerza la re-serialización completa de cada parte, que es el interruptor a alcanzar cuando quiere que la salida refleje solo lo que contiene el modelo de objetos
La ruta de copia cruda usa la misma verificación de archivo que LoadSheet: el origen debe seguir existiendo con el tamaño y la marca de tiempo vistos en Open. Cuando no, SaveAs serializa esas partes de forma normal y el archivo sigue completo. Dos casos toman siempre la ruta de serializar: un libro cuyo origen fue un .xlsb binario, que se convierte a XML al leer, y un guardado con StrictOOXML puesto, donde los namespaces de las partes cambian
Guardar un libro parcialmente cargado necesita una decisión. Para un origen .xlsb, SaveAs se niega mientras alguna hoja siga diferida y registra «Load every deferred binary worksheet before saving» en Diagnostics. Para un .xlsx, trate un libro cargado selectivamente como una vista de lectura: una hoja stub no tiene celdas que el escritor pueda emitir, así que o llame a LoadSheet por cada stub primero o reabra sin selección antes de editar y guardar
Un pipeline de descubrimiento que reabre a propósito
El patrón que aguanta tiene dos pasadas. La pasada uno abre cada archivo solo con metadatos y decide. La pasada dos reabre solo los archivos que coincidieron, con una selección de hojas, y hace el trabajo. Reabrir parece desperdicio hasta que cuenta lo que la pasada uno evitó: el segundo Open es el único que parsea celdas, y parsea solo las hojas que la pasada uno eligió
procedure ProcessInvoiceFile(const FileName: WideString);
var
Probe, Workbook: TXLSXWorkbook;
Wanted: TStringList;
I: Integer;
begin
Wanted := TStringList.Create;
try
// Pasada 1: descubrimiento, sin celdas
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 en este archivo vale la pena parsear
// Pasada 2: celdas solo para las hojas elegidas
Workbook := TXLSXWorkbook.Create;
try
Workbook.SelectedSheets.Assign(Wanted);
if Workbook.Open(FileName) = 1 then
PostInvoices(Workbook); // su propio código de procesamiento
finally
Workbook.Free;
end;
finally
Wanted.Free;
end;
end;
Limpiar MetadataOnly después de Open no convierte la sonda en un libro completo. La bandera se lee al abrir; cambiarla después solo afecta al próximo Open sobre ese objeto. La sonda todavía puede crecer una hoja a la vez vía LoadSheet, y para una o dos hojas eso es razonable, pero a un pipeline lo sirve mejor la re-apertura limpia, que además trae consigo los comentarios y tablas de cada hoja
Dónde encaja esto con las demás palancas de archivos grandes
La carga selectiva recorta lo que usted parsea. No hace nada por el costo de una hoja una vez cargada, y no puede ayudar cuando el trabajo necesita cada celda de cada hoja. Ese caso es de almacenamiento, cubierto en el artículo sobre almacenamiento de celdas por bloques de filas y estilos por rango, y de throughput de parseo, cubierto en parseo paralelo de XLSX y el allocador de memoria. Cuando la tarea es una lectura pura sin ninguna edición, el lector directo con streaming se salta el modelo de objetos del libro por completo y es mejor herramienta que cualquier estrategia de carga que construya uno
Todo esto corre dentro de su propio ejecutable Delphi o C++Builder, sin instalación de Excel en el servidor que hace el escaneo. Los formatos soportados, las ediciones y las licencias están listados en la página del componente de hojas de cálculo HotXLS para Delphi