HotXLS, la biblioteca Excel nativa para Delphi y C++Builder, lee el valor que Excel ya guardó junto a una fórmula a través de TryGetCachedFormulaValue e IXLSFormulaCacheReader. Ninguno de los dos puntos de entrada llama a la calculadora, decompila tokens de fórmula, actualiza estados dirty ni escribe nada de vuelta en el modelo, así que un libro que usted solo lee queda exactamente como lo abrió
El escenario que motiva esto es aburrido y sumamente común. Un trabajo nocturno abre unos cuantos cientos de libros producidos por otras personas, saca una columna de totales de cada uno y empuja los números a un almacén de datos. Los totales ya están en los archivos — Excel los calculó y los guardó. Pero en el momento en que el trabajo le pide el valor a una celda de fórmula, una biblioteca que solo tiene una respuesta para esa pregunta construye un grafo de dependencias y evalúa toda la hoja, y un trabajo que debería estar limitado por E/S se convierte en un benchmark de cálculo
¿Por qué leer una celda de fórmula cuesta un recálculo completo?
Porque un getter de valor sobre una celda de fórmula es una petición de producir un valor, y la única forma universalmente correcta de producirlo es evaluar la fórmula. Ese es el default correcto para una aplicación que edita libros, y el default equivocado para una pipeline que los extrae. Peor aún, la evaluación no está libre de efectos secundarios: escribe resultados de vuelta en las celdas, voltea flags dirty, y puede resolverse de forma distinta a la aplicación productora cuando una función no está soportada o una referencia externa está rota. Un trabajo que usted describió a su equipo de operaciones como de solo lectura produce silenciosamente un libro que ya no coincide con el del disco, y si algo lo guarda más tarde, el archivo en disco también cambia
La lectura de valores en caché es la otra mitad del contrato. Responde una pregunta más estrecha — ¿qué guardó aquí la aplicación productora? — y se niega a responder cualquier otra. Cuando de verdad quiere números frescos, HotXLS igual le ofrece el recálculo incremental guiado por un grafo de dependencias; el punto es que extracción y evaluación deben ser dos llamadas distintas, no una sola llamada con dos estados de ánimo
Tres hechos ortogonales sobre una celda
Primero la conclusión: un valor de fórmula en caché lleva tres hechos independientes, y colapsarlos en un solo Variant pierde información que usted necesita. TXLSFormulaCacheInfo los mantiene separados como State, Kind y Value. TXLSFormulaCacheState registra la procedencia en cinco casos — xlfcsNotFormula, xlfcsMissing, xlfcsLoaded, xlfcsCalculated y xlfcsInvalidated — mientras que TXLSFormulaCacheValueKind clasifica el payload como xlfcvBlank, xlfcvNumber, xlfcvDateTime, xlfcvString, xlfcvBoolean o xlfcvError. Esta separación es lo que permite reportar la presencia con honestidad: un blanco en caché, una cadena vacía en caché, un False en caché, un cero en caché y un error en caché son todos valores reales, así que la presencia jamás puede inferirse de VarIsEmpty o VarIsNull. TryGetCachedFormulaValue devuelve True solo para xlfcsLoaded y xlfcsCalculated, y aun cuando devuelve False llena un estado diagnosticable
var
Book: TXLSXWorkbook;
Info: TXLSFormulaCacheInfo;
begin
Book := TXLSXWorkbook.Create;
try
Book.Open('quarterly-model.xlsx');
// SheetIndex, Row y Col son todos base uno aquí
if Book.TryGetCachedFormulaValue(1, 12, 5, Info) then
Writeln('cached value: ', VarToStr(Info.Value))
else
Writeln('no usable cache, state ordinal ', Ord(Info.State));
finally
Book.Free;
end;
end;
¿Por qué falta el valor en caché?
Hay exactamente cuatro razones por las que TryGetCachedFormulaValue devuelve False, y el estado le dice cuál aplica. xlfcsNotFormula significa que la celda contiene un literal o nada en absoluto, y que las coordenadas estén fuera de rango colapsa en la misma respuesta. xlfcsMissing significa que la celda sí es una fórmula pero el productor no guardó payload de valor para ella — un desenlace común cuando un generador escribe fórmulas y deja que Excel llene los resultados al primer arranque. xlfcsInvalidated significa que el texto de la fórmula fue reemplazado tras la carga, así que el valor que estaba ahí describe una expresión que ya no existe. xlfcsCalculated, en cambio, es un caso de éxito: marca un valor que su propio código o el evaluador de HotXLS produjo durante esta sesión, a diferencia de xlfcsLoaded, que vino del archivo
La honestidad sobre una caché faltante importa más que taparla. HotXLS se niega a inventar un valor, y al guardar es igual de estricto — solo xlfcsLoaded y xlfcsCalculated emiten un valor en caché, mientras que xlfcsMissing y xlfcsInvalidated escriben la fórmula sola en lugar de congelar un número añejo en el archivo. Eso le deja tres respuestas sanas en una pipeline: saltarse la fila y registrar el hueco, recalcular deliberadamente ese libro y aceptar el costo, o evaluar y reconciliar. Si el número evaluado discrepa de lo que la aplicación productora habría escrito, el tracer de evaluación de fórmulas es la herramienta para averiguar dónde divergen los dos cálculos, en lugar de adivinar desde el resultado
Un lector para los motores clásico, OOXML y ODF
Una pipeline no debería importarse de si el archivo que acaba de abrir era BIFF, OOXML o ODF. IXLSFormulaCacheReader es el único punto de entrada de solo lectura para los tres: tanto TXLSWorkbook.CreateFormulaCacheReader como TXLSXWorkbook.CreateFormulaCacheReader devuelven un adaptador ligero sobre la búsqueda dispersa de celdas que cada motor ya usa, con coordenadas idénticas de hoja, fila y columna base uno. Las clases de libro deliberadamente no implementan la interfaz ellas mismas — una referencia de interfaz al libro cambiaría su semántica de propiedad y dejaría que los llamadores se colaran más allá del lease de tiempo de vida. En su lugar, destruir el libro limpia el puntero crudo dentro de ese lease, y cualquier lector que su código aún sostenga lanza EXLSFormulaCacheReaderInvalidated en su siguiente consulta en lugar de desreferenciar memoria liberada. Es verificación de tiempo de vida fail-fast, no una garantía de concurrencia
var
Reader: IXLSFormulaCacheReader;
Info: TXLSFormulaCacheInfo;
Row, Missing, Errors: Integer;
Total: Double;
begin
Reader := Book.CreateFormulaCacheReader;
Total := 0;
Missing := 0;
Errors := 0;
for Row := 2 to LastRow do
if Reader.TryGetCachedFormulaValue(1, Row, 7, Info) then
begin
case Info.Kind of
xlfcvNumber: Total := Total + Double(Info.Value);
xlfcvError: Inc(Errors);
end;
end
else if Info.State = xlfcsMissing then
Inc(Missing);
// No corrió ninguna calculadora, ningún flag dirty se movió, Book no cambió
end;
Dónde viven realmente los bytes en caché
Para los archivos .xls clásicos la caché es el campo FormulaValue del registro Formula, ocho bytes descritos por [MS-XLS] §2.5.133. Cuando la palabra alta equivale a $FFFF el payload no es un double IEEE 754 sino un variant etiquetado, y la disposición es fácil de equivocar sutilmente: el tipo del variant está en val[0] y el payload booleano o BErr está en val[2], con val[1] indefinido. HotXLS antes leía el payload desde val[1], que es el tipo de desplazamiento de uno que solo aparece en los archivos específicos que guardan en caché un booleano o un error en lugar de un número. El lector y el escritor de fórmulas compartidas ahora concuerdan en los mismos offsets, así que un TRUE en caché sobrevive a una carga y guardado intacto en lugar de decaer en ruido
La fidelidad de tipos en los formatos de paquete es un problema aparte con su propia trampa. En OOXML el valor en caché cuelga del elemento c como <v>, con el atributo t nombrando el tipo según ECMA-376 Parte 1 §18.3.1.4. HotXLS lee t="e" directo a un Variant varError y lo mapea de vuelta al texto de error estándar al guardar, así que los errores jamás se disfrazan de enteros ordinarios — pero el RTL de Delphi no le ayudará aquí, porque VarAsType(Integer, varError) lanza una excepción de conversión. La construcción que funciona establece TVarData.VType y TVarData.VError directamente. Las fechas siguen la misma disciplina en la dirección opuesta: t="d" y el tipo de valor de fecha ODF son declaraciones explícitas de tipo y se convierten en varDate, mientras que una caché numérica BIFF no lleva bandera de fecha alguna y por tanto permanece como Double. HotXLS nunca adivina una fecha a partir del formato numérico de una celda, porque el formato numérico es presentación y la caché es datos. ODF añade un caso más que conviene conocer — office:value-type="void" expresa una caché que está presente pero no lleva valor, y como ODF no tiene tipo de valor de error, el texto con apariencia de error se preserva como texto en lugar de promoverse a error
function DescribeCache(const Info: TXLSFormulaCacheInfo): string;
begin
case Info.State of
xlfcsNotFormula: Result := 'not a formula cell';
xlfcsMissing: Result := 'formula stored with no cached value';
xlfcsInvalidated: Result := 'formula replaced since load';
else
case Info.Kind of
xlfcvError: Result := 'error code ' + IntToStr(TVarData(Info.Value).VError);
xlfcvDateTime: Result := DateTimeToStr(VarToDateTime(Info.Value));
xlfcvBoolean: Result := BoolToStr(Info.Value, True);
xlfcvNumber: Result := FloatToStr(Double(Info.Value));
xlfcvString: Result := VarToStr(Info.Value);
else
Result := 'present but blank';
end;
end;
end;
¿Las fórmulas compartidas comparten sus valores en caché?
No, y asumir lo contrario es la manera en que un barrido acaba reportando el mismo número para una columna entera. Una fórmula compartida OOXML comparte solo la expresión de la fórmula y la optimización de almacenamiento; cada celda miembro sigue siendo dueña de su propio <v>. HotXLS por eso nunca propaga la caché del miembro raíz a un seguidor que llegó sin valor, y un seguidor que cargó como xlfcsMissing sigue reportando xlfcsMissing tras un guardado y reapertura. Si está trabajando cómo se almacena y expande el grupo en primer lugar, la mecánica del atributo si de la fórmula compartida y su expansión se cubre por separado; para lectura de caché, la regla se reduce a una línea — pregunte a cada celda, no confíe en nada que no haya preguntado
La lectura de valores en caché, el lector unificado entre motores y el motor de recálculo que usted puede elegir no invocar se entregan todos en el HotXLS Delphi Spreadsheet Component estándar para Delphi y C++Builder, sin dependencia de Excel ni de ningún servidor de automatización OLE; la página del producto lleva la referencia completa de la API de los puntos de entrada de libro y lector mostrados aquí