HotXLS puede reescribir una hoja de cálculo dentro de un paquete XLSX existente sin analizar ni volver a comprimir el resto del archivo. TXLSDirectWriter.BeginPatch abre un paquete de origen, copia cada entrada excepto la hoja de destino con sus bytes comprimidos tal cual, y permite reescribir esa única hoja mediante las llamadas habituales AddSheet, AddRow y Write*. Los gráficos, las cachés de tablas dinámicas, los temas, los estilos y las cadenas compartidas nunca se descomprimen en absoluto
El flujo de trabajo que esto resuelve aparece en informes y actualización de datos. Un libro de trabajo llega de un equipo de negocio con tablas dinámicas, segmentaciones de datos, formatos condicionales y una década de formato acumulado. Cada noche hay que reemplazar una hoja de datos con cifras nuevas. Cargar y volver a guardar todo el libro de trabajo cuesta minutos por archivo y, más importante aún, arriesga la fidelidad de funciones que el motor de carga tiene que reconstruir. Parchar evita ambos problemas al no tocar lo que no necesita tocar
¿Por qué es interesante copiar bytes comprimidos?
Una entrada zip que se copia a nivel comprimido cuesta una copia de flujo. La misma entrada procesada por una ruta de escritura normal cuesta una inflación al entrar y una deflación al salir, y la deflación es la mitad costosa. En un libro de trabajo con una caché de tabla dinámica grande y unas cuantas docenas de imágenes incrustadas, esa diferencia es la diferencia entre un parche que termina en el tiempo que toma escribir la hoja nueva y uno que pasa la mayor parte de su tiempo recomprimiendo bytes que nunca examinó
HotXLS usa CopyCompressedFrom para esto, que escribe los bytes comprimidos de la entrada de origen directamente en el archivo de destino. Cuando una entrada no puede copiarse de esa forma, porque usa un método de compresión distinto o cifrado débil, el escritor recurre a una copia de flujo descomprimida en lugar de fallar. Las entradas marcadoras de directorio se omiten, ya que el escritor produce las suyas propias
Reemplazar en el mismo archivo, o escribir a un archivo nuevo
Dos sobrecargas cubren las dos formas que toma esta tarea. La forma en el mismo archivo prepara el resultado en un archivo temporal junto al original, cierra el descriptor de origen, y luego elimina y renombra, de modo que una falla a mitad de la escritura deja intacto el original. La forma con destino explícito deja el origen sin tocar y puede reemplazar una hoja o agregar una nueva:
var
W: TXLSDirectWriter;
begin
W := TXLSDirectWriter.Create;
try
W.BeginPatch('monthly-dashboard.xlsx', 'Data'); // en el mismo archivo
W.AddSheet('Data');
W.AddRow(1);
W.WriteString(1, 'Region');
W.WriteString(2, 'Revenue');
W.AddRow(2);
W.WriteString(1, 'North');
W.WriteNumber(2, 184320.55);
W.AddRow(3);
W.WriteFormula(1, '=SUM(B2:B2)');
W.Close;
finally
W.Free;
end;
end;
La variante de inserción toma una ruta de origen y una de destino, más InsertSheet:
// El origen queda sin tocar; el destino recibe una hoja adicional llamada Extra
W.BeginPatch('template.xlsx', 'output.xlsx', 'Extra', True);
W.AddSheet('Extra');
W.AddRow(1);
W.WriteString(1, 'appended by the nightly job');
W.Close;
La inserción es la parte que requiere una cirugía contable real. El escritor analiza el registro de hojas en xl/workbook.xml y el mapa de relaciones que vincula cada hoja con su parte, y luego elige el siguiente número de parte libre, el identificador de hoja y el identificador de relación. Los tipos de relación siguen las convenciones del paquete de origen, así que parchar un libro de trabajo ISO 29500 strict emite tipos de relación strict, y parchar uno transicional emite tipos transicionales
Qué descarta y restringe el parche deliberadamente
La cadena de cálculo se descarta en ambos modos. En modo de reemplazo, sus entradas describen celdas de una hoja que ya no existe en esa forma; en modo de inserción, el desplazamiento del índice de hoja la invalida de plano. Excel reconstruye la cadena en el siguiente recálculo, así que descartarla es correcto y no una pérdida. Esa parte se deja fuera de la copia, y su entrada de relación y su anulación de tipo de contenido se eliminan quirúrgicamente
Dos semánticas de autoría cambian dentro de un parche, y ambas se derivan del mismo principio: el parche no debe perturbar las partes que no reescribió. Las cadenas se escriben en línea dentro de la hoja en lugar de agregarse a la tabla de cadenas compartidas, porque la tabla de origen cruza sin tocar. Y StyleIndex se refiere a entradas en el cellXfs del paquete de origen, no a una tabla de estilos que construya el escritor. Eso significa que se pueden referenciar formatos que el libro de trabajo original ya define, que suele ser exactamente lo que quiere una actualización de datos, pero también significa que hay que saber qué índice lleva qué formato
// Dentro de un parche, StyleIndex indexa el cellXfs del paquete DE ORIGEN.
// Una fecha necesita un índice explícito que se corresponda ahí con un formato de fecha:
W.WriteDateTime(3, EncodeDate(2026, 8, 22), DateStyleIndexFromTemplate);
// La sobrecarga de WriteDateTime sin estilo se rechaza en modo parche,
// porque asume la propia tabla de estilos del escritor, que un parche
// nunca crea
Seis puntos de entrada de autoría están bloqueados: agregar tablas, gráficos, imágenes, comentarios, nombres definidos y estilos de celda generan todos una excepción en modo parche, con una segunda red de seguridad al cerrar que falla si alguno de sus contadores es distinto de cero. Cada una de esas funciones requeriría editar partes que el parche copia tal cual, y un paquete a medio editar es peor que una operación rechazada. Exactamente una hoja puede parcharse por operación
Cuándo parchar y cuándo cargar
Parchar es la herramienta correcta cuando el libro de trabajo es grande, el cambio se limita a una hoja, y el resto del archivo debe sobrevivir bit por bit. Es la herramienta incorrecta cuando el cambio abarca varias hojas, cuando se necesita formato nuevo u objetos nuevos, o cuando el archivo es lo bastante pequeño como para que una carga y guardado normales no cuesten nada. Para la generación masiva desde cero, la ruta de flujo descrita en el escritor directo en flujo sigue siendo la mejor opción, y comparte la misma API de AddRow y Write*, así que moverse entre ambas es mecánico
La manipulación a nivel de hoja dentro de un libro de trabajo cargado, cuando sí se necesita el modelo de objetos completo, se cubre en duplicación de hojas de cálculo en paquetes XLSX. Y si la razón por la que estás considerando un parche es que el procesamiento de todo el libro de trabajo se ha vuelto lento, vale la pena leer las mediciones y el comportamiento de memoria en rendimiento de libros de trabajo grandes antes de elegir un enfoque
Verificar que un parche realmente hizo lo que crees
Tres verificaciones detectan casi todos los errores. Confirma que las partes que esperabas que sobrevivieran siguen en el archivo, que xl/calcChain.xml desapareció, y que volver a abrir el archivo mediante TXLSXWorkbook reporta el número de hojas esperado, sin cambios para un reemplazo e incrementado en uno para una inserción. Volver a leer la hoja parchada y comparar algunos valores y fórmulas cierra el ciclo
Vale la pena repetir un detalle de implementación surgido del desarrollo de esta función, porque puede afectar a cualquiera que escriba código similar a nivel de zip. Los nombres de las partes de hoja de cálculo se comparan por prefijo, y un error de uno en la longitud del prefijo hace que el predicado nunca coincida, así que una parte recién escrita choca con un nombre existente y los lectores que toman la última entrada con un nombre dado eligen en silencio la hoja equivocada. Si un parche parece haber intercambiado el contenido de dos hojas, revisa la coincidencia de nombres antes de revisar el XML
El parcheo en el mismo archivo, la escritura en flujo y el modelo de objetos completo del libro de trabajo se distribuyen en la misma biblioteca para Delphi y C++Builder; la lista de funciones está en la página del componente de hoja de cálculo para Delphi HotXLS