El ERP aguas abajo rechazó el feed nocturno con un error de análisis en la columna de totales. El CSV se veía bien en un editor de texto hasta la fila 42, donde el campo de importe decía =SUM(D2:D41): la fórmula misma, como texto, no el número que calcula. Nada estaba roto; ese es el comportamiento documentado. La exportación CSV en HotXLS serializa el modelo de celdas tal como está, y una celda de fórmula cuyo valor nunca se calculó no tiene nada que entregar salvo el texto de la fórmula. Todo equipo que conecta las llamadas de exportación de la biblioteca a un pipeline de integración encuentra alguna versión de esto en la primera semana, así que es el lugar correcto para empezar.
Por qué su CSV contiene fórmulas en lugar de números
HotXLS mantiene el texto de fórmula y el valor calculado como cosas separadas, y SaveAsCSV deliberadamente no ejecuta el motor de cálculo: una exportación no debe mutar el libro ni quedarse detenida por una cadena patológica de fórmulas. Los archivos guardados por Excel llevan resultados en caché junto a las fórmulas, así que exportarlos funciona como se espera; la trampa aparece con libros generados por su propio código, donde se escribieron fórmulas pero nunca se evaluaron. La solución es hacer que los valores existan antes de exportar, usando el mismo motor Calculate que maneja referencias entre hojas y funciones personalizadas:
var
Book: TXLSXWorkbook;
Sheet: TXLSXWorksheet;
R: Integer;
begin
Book := TXLSXWorkbook.Create;
try
Book.Open('invoice-run.xlsx');
Sheet := Book.Sheets[0];
// Materialize formula results so the CSV carries numbers, not '=...' text
for R := 2 to 41 do
if Sheet.Cells[R, 4].Formula <> '' then
Sheet.Cells[R, 4].Value := Book.Calculate(Sheet.Cells[R, 4].Formula);
Book.SaveAsCSV('feed.csv', 0, ','); // sheet 0, comma
Book.SaveAsCSV('feed.tsv', 0, #9); // same sheet as TSV
finally
Book.Free;
end;
end;
Noten lo que hace el bucle: sobrescribe las celdas de fórmula con sus valores calculados, lo cual es exactamente correcto para una pasada de exportación descartable, pero incorrecto si luego planean guardar nuevamente el libro como .xlsx. Exporten desde una copia, o limiten la escritura de vuelta a la ejecución de exportación. Las capacidades más profundas del motor detrás de Calculate, incluido el registro de sus propias funciones, se cubren en el motor de fórmulas de HotXLS y funciones personalizadas.
Lo que garantiza el escritor delimitado
La ruta CSV produce UTF-8 con byte order mark, finales de línea CRLF y comillas RFC 4180: los campos que contienen el delimitador, comillas o saltos de línea se envuelven, y las comillas internas se duplican. Las fechas se renderizan como yyyy-mm-dd hh:nn:ss sin importar el formato visual de la celda, que es la elección sensata para consumidores máquina pero sorprende a quienes esperan el formato en pantalla. Las celdas con texto enriquecido se aplanan concatenando sus runs.
Estos valores predeterminados resuelven la mayoría de las discusiones con importadores antes de que empiecen, con dos advertencias que conviene escribir en el contrato de interfaz. Primero, el BOM: es lo que hace que Excel abra el archivo con acentos correctos, pero algunos parsers estrictos tratan esos tres bytes como datos; si el suyo lo hace, quítenlos en la entrega. Segundo, TSV no es una función separada; es el mismo escritor con #9 como parámetro de delimitador, así que todo lo anterior aplica de forma idéntica. La hoja se selecciona por índice 0-based en el overload de varios argumentos, mientras que el atajo de un solo argumento SaveAsCSV(FileName) toma la hoja activa.
La exportación HTML es una instantánea, no un formato de intercambio
Donde CSV descarta todo salvo valores, SaveAsHTML intenta preservar la apariencia: una <table> por hoja, regiones combinadas expresadas como colspan y rowspan, y estilos básicos de celda inline como CSS. Los colores relativos al tema se omiten en vez de resolverse, así que una plantilla corporativa que depende de ranuras de tema saldrá más plana de lo que se ve en Excel; usen colores RGB explícitos en todo lo que deba sobrevivir. El objeto de opciones controla el contenedor:
var
Opts: TXLSXHtmlExportOptions;
begin
Opts := TXLSXHtmlExportOptions.Create;
try
Opts.Title := 'Weekly settlement';
Opts.TableClass := 'report-grid'; // hook for the host page stylesheet
Opts.WriteDocument := True; // full page, not a fragment
if Book.SaveAsHTML('settlement.html', 0, Opts) <> 0 then
raise Exception.Create('Sheet index out of range');
finally
Opts.Free;
end;
end;
Dos detalles de ese fragmento merecen atención. WriteDocument := False cambia la salida a un fragmento de tabla desnudo, que es lo que necesitan al inyectar una vista previa en una página existente: definan TableClass y dejen que la hoja de estilos envolvente haga el tema. Y la convención de retorno es la opuesta de la mayoría de las llamadas de HotXLS: SaveAsHTML devuelve 0 en éxito y -1 para un índice de hoja inválido, así que una comprobación de éxito por hábito con = 1 reportará mal cada exportación. Para enviar por correo o incrustar solo una región en lugar de una hoja, TXLSXRange.SaveAsHTML exporta cualquier bloque rectangular con las mismas reglas de renderizado.
Salida RTF y dónde aún gana su lugar
El cuarto destino de exportación escribe tablas RTF 1.6, una hoja por llamada mediante SaveAsRTF. Los anchos de columna se aproximan a unos 96 twips por carácter de ancho de columna y, esta es la limitación estructural, las celdas combinadas no abarcan columnas en la salida: solo la celda ancla lleva contenido y las celdas cubiertas salen como blancos. Eso hace que RTF sea inadecuado para plantillas con mucho diseño, pero sigue siendo la ruta de menor resistencia para pegar resultados tabulares en documentos de procesador de texto y sistemas heredados de gestión documental anteriores a la ingesta HTML.
Round-tripping: importar CSV es destructivo por diseño
La dirección de importación tiene su propio contrato. OpenCSV limpia todo el libro y lo reconstruye como una sola hoja llamada Sheet1: en espíritu es un constructor, no una fusión, así que nunca lo llamen sobre un libro con contenido sin guardar. Pasar #0 como separador activa la detección automática de delimitador, y la bandera ADetectTypes controla la promoción de tipos: con ella activada, cadenas numéricas se vuelven números, cadenas ISO-8601 se vuelven fechas y true/false se vuelven booleanos. Desactívenla cuando el feed contenga identificadores con ceros a la izquierda o códigos postales, que la promoción de tipos corrompe en silencio convirtiéndolos en números. Ambas fachadas de libro exponen la misma importación; combínenla con las llamadas de exportación anteriores y tendrán un puente de formatos que no necesita instalación de Excel en ninguna parte del pipeline, el escenario explorado más a fondo en generación de reportes de base de datos a Excel con HotXLS.
Exportar directo a un stream
Todos los escritores tratados aquí, CSV, HTML, RTF y los propios formatos de libro, tienen overloads de stream junto a las versiones con nombre de archivo, y en código de servidor esos son los que conviene usar. Un endpoint web que produce una descarga CSV puede escribir en un TMemoryStream y entregarlo directamente al objeto de respuesta: sin archivo temporal, sin trabajo de limpieza, sin colisión entre dos solicitudes que exportan con el mismo nombre generado. Lo mismo aplica a depositar exportaciones en blob storage o adjuntarlas a correo saliente: el sistema de archivos deja de formar parte del pipeline.
Este patrón se suma a la propiedad central de despliegue de la biblioteca: ambas fachadas son lectores y escritores nativos de Object Pascal, así que no hay instalación de Excel, no hay automatización COM y no hay cuello de botella de un solo hilo por proceso en el servidor. Cada solicitud puede tener su propio objeto de libro, ejecutar la escritura de valores calculados de la primera sección y transmitir la exportación, totalmente en paralelo con sus vecinas. El único recurso a vigilar es la memoria: el modelo de libro se mantiene en RAM durante la exportación, así que un servicio que abre libros muy grandes solo para reemitirlos como CSV debe limitar trabajos concurrentes o encolar los sobredimensionados en vez de dejar que el tráfico pico decida el working set.
Establezcan IncludeBOM en las opciones HTML cuando el fragmento se vaya a guardar como archivo independiente consumido por herramientas que detectan codificación, y dejen la declaración charset del content-type a la capa HTTP cuando sirvan directamente.
FAQ: exportación de hojas de cálculo desde Delphi
¿Cómo exporto una hoja específica en lugar de la activa? Usen los overloads que reciben índice de hoja: SaveAsCSV(FileName, SheetIndex, Delimiter), SaveAsHTML(FileName, SheetIndex, Options), SaveAsRTF(FileName, SheetIndex). El índice es 0-based en la fachada XLSX.
Excel muestra caracteres acentuados rotos al abrir mi CSV exportado. ¿Por qué? Casi seguro no es el archivo: el escritor emite un BOM UTF-8 precisamente para que Excel lo decodifique correctamente. Revisen si un paso intermedio, como una transferencia FTP en modo texto o una copia de stream que quita los primeros bytes, dañó el BOM.
¿Puedo exportar solo un rango de celdas a CSV? En la fachada XLS, sí: IXLSRange tiene sus propios SaveAsCSV, SaveAsHTML y SaveAsRTF. En la fachada XLSX, la exportación a nivel rango está disponible para HTML mediante TXLSXRange.SaveAsHTML.
Las exportaciones son donde la semántica del libro se encuentra con los parsers de otros sistemas, así que los detalles de contrato anteriores pertenecen a su documentación de integración, no solo a su código. La referencia completa de métodos está en la página de producto HotXLS Component.