Immagina un job notturno che costruisce via codice una cartella di lavoro di fatturazione e la scrive come CSV perché un sistema a valle la importi. I numeri sembrano corretti in Excel. Il CSV si apre senza problemi in un editor di testo. Poi l'importatore si blocca sulla colonna dei totali, perché il campo importo della riga 42 riporta =SUM(D2:D41), la formula come testo letterale, non il valore che dovrebbe calcolare. Niente è rotto. Questo è un comportamento documentato, ed è la prima cosa da capire sull'esportazione da HotXLS: il writer serializza il modello di cella esattamente com'è, e una cella con formula il cui valore non è mai stato calcolato ha solo il testo della formula da consegnare
Perché il tuo CSV contiene formule invece di numeri
HotXLS memorizza il testo della formula e il valore calcolato come due cose separate. SaveAsCSV non esegue il motore di calcolo in uscita, per progettazione: un'esportazione non dovrebbe modificare la cartella di lavoro, e non dovrebbe rischiare di bloccarsi su una catena di formule patologica. I file salvati da Excel stesso portano risultati in cache accanto alle formule, quindi riesportarli si comporta come ci si aspetta. La trappola è specifica delle cartelle di lavoro generate dal tuo stesso codice, dove le formule sono state scritte ma mai valutate. La soluzione è far esistere i valori prima di esportare, usando lo stesso motore Calculate che risolve i riferimenti tra fogli e le funzioni personalizzate:
var
Book: TXLSXWorkbook;
Sheet: TXLSXWorksheet;
R: Integer;
begin
Book := TXLSXWorkbook.Create;
try
Book.Open('invoice-run.xlsx');
Sheet := Book.Sheets[0];
// Materializza i risultati delle formule in modo che il CSV contenga numeri, non testo '=...'
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, ','); // foglio 0, virgola
Book.SaveAsCSV('feed.tsv', 0, #9); // stesso foglio come TSV
finally
Book.Free;
end;
end;
Osserva cosa fa realmente il ciclo: sovrascrive le celle con formula con i loro valori calcolati. Questo è esattamente corretto per un passaggio di esportazione usa e getta, ed è sbagliato se intendi salvare di nuovo la cartella di lavoro come .xlsx in seguito, perché hai appena sostituito formule vive con numeri congelati. Esporta da una copia, oppure limita la riscrittura in modo che tocchi solo l'esecuzione dell'esportazione. Il motore dietro Calculate va oltre questo, incluso registrare funzioni personalizzate, argomento trattato in il motore di formule HotXLS e le funzioni personalizzate
Cosa garantisce il writer delimitato
Il percorso CSV produce UTF-8 con byte order mark, terminatori di riga CRLF e quoting RFC 4180. Qualsiasi campo che contiene il delimitatore, una virgoletta o un'interruzione di riga viene racchiuso tra virgolette, e le virgolette incorporate vengono raddoppiate. Le date vengono rese come yyyy-mm-dd hh:nn:ss indipendentemente dal formato di visualizzazione della cella. È la scelta giusta per un consumatore macchina, anche se sorprende chiunque si aspettasse che la formattazione a schermo venisse riportata. Le celle con rich text vengono appiattite concatenando i loro run
Questi default risolvono la maggior parte dei conflitti con un importatore prima ancora che inizino, ma due di essi vanno comunque inclusi nel tuo contratto di interfaccia. Il primo è il BOM. È ciò che permette a Excel di aprire il file con i caratteri accentati intatti, eppure una manciata di parser rigorosi tratta quei tre byte come dati; se il tuo è uno di questi, rimuovili nella consegna. Il secondo è il TSV. Non è affatto una funzionalità separata, solo lo stesso writer chiamato con #9 come delimitatore, quindi tutto quanto sopra si applica invariato. Il foglio da esportare viene scelto tramite indice a base 0 nell'overload multi-argomento, mentre la forma abbreviata a singolo argomento SaveAsCSV(FileName) usa il foglio attivo
L'esportazione HTML è un'istantanea, non un formato di interscambio
Mentre il CSV scarta tutto tranne i valori, SaveAsHTML cerca di mantenere l'aspetto: una <table> per foglio, le regioni unite espresse come colspan e rowspan, uno stile di cella di base incorporato come CSS. I colori relativi al tema vengono saltati invece che risolti, quindi un template che si appoggia agli slot del tema risulta più spoglio di come appare in Excel. Imposta colori RGB espliciti su tutto ciò che deve sopravvivere al viaggio. L'oggetto delle opzioni controlla l'involucro:
var
Opts: TXLSXHtmlExportOptions;
begin
Opts := TXLSXHtmlExportOptions.Create;
try
Opts.Title := 'Weekly settlement';
Opts.TableClass := 'report-grid'; // aggancio per il foglio di stile della pagina ospitante
Opts.WriteDocument := True; // pagina completa, non un frammento
if Book.SaveAsHTML('settlement.html', 0, Opts) <> 0 then
raise Exception.Create('Sheet index out of range');
finally
Opts.Free;
end;
end;
Due dettagli in questo frammento meritano attenzione. Imposta WriteDocument su False e l'output diventa un semplice frammento di tabella invece di una pagina completa, il che è ciò che vuoi quando inietti un'anteprima in un layout esistente: imposta TableClass e lascia che sia il foglio di stile ospitante a occuparsi della grafica. La convenzione del valore restituito è anche l'opposto della maggior parte delle chiamate HotXLS. SaveAsHTML restituisce 0 in caso di successo e -1 per un indice di foglio non valido, quindi un controllo abitudinario su = 1 segnalerà come fallimento ogni esportazione riuscita. Quando ti serve una regione invece di un intero foglio, magari per inviare via email o incorporare un singolo blocco, TXLSXRange.SaveAsHTML esporta qualsiasi intervallo rettangolare secondo le stesse regole di rendering
Output RTF e dove trova ancora il suo posto
Il quarto target scrive tabelle RTF 1.6, un foglio per chiamata tramite SaveAsRTF. Le larghezze di colonna sono approssimate a circa 96 twip per carattere di larghezza colonna. La limitazione strutturale da conoscere è che le celle unite non si estendono nell'output: solo la cella ancora porta il proprio contenuto, e le celle coperte vengono emesse vuote. Questo esclude l'RTF per i template ricchi di layout. Trova comunque il suo posto come percorso di minor resistenza per riversare risultati tabellari in un elaboratore di testi o in un sistema legacy di gestione documentale precedente all'adozione dell'HTML
Andata e ritorno: importare CSV è distruttivo per progettazione
Rileggere il CSV ha un proprio contratto. OpenCSV cancella l'intera cartella di lavoro e la ricostruisce come un unico foglio chiamato Sheet1. È un costruttore nello spirito, non un merge, quindi non va mai chiamato su una cartella di lavoro che contiene ancora dati non salvati. Passare #0 come separatore attiva il rilevamento automatico del delimitatore. Il flag ADetectTypes controlla la promozione di tipo: quando attivo, le stringhe numeriche diventano numeri, le stringhe ISO-8601 diventano date, e true/false diventano booleani. Disattivalo quando il feed contiene identificativi con zeri iniziali, codici postali o codici prodotto, che la promozione trasforma silenziosamente in numeri (uno zero iniziale semplicemente scompare nel momento in cui 00123 diventa 123). Entrambe le facciate espongono la stessa importazione. Abbinala alle chiamate di esportazione viste sopra e ottieni un ponte tra formati che non richiede Excel installato in nessun punto della pipeline, lo scenario trattato in la generazione di report Excel da database con HotXLS
Esportare direttamente in uno stream
Ogni writer qui presente ha un overload per stream accanto alla versione con nome file: CSV, HTML, RTF e gli stessi formati di cartella di lavoro. Nel codice lato server sono questi gli overload da usare. Un endpoint web che serve un download CSV può scrivere in un TMemoryStream e passarlo direttamente all'oggetto di risposta, senza file temporaneo, senza job di pulizia, e senza collisioni tra due richieste che per caso hanno scelto lo stesso nome generato. Lo stesso vale per inviare le esportazioni verso un blob storage o allegarle a posta in uscita. Il file system esce completamente dal quadro
Questo schema si combina bene con il modo in cui la libreria viene distribuita. Entrambe le facciate sono reader e writer Object Pascal nativi, quindi non c'è installazione di Excel, non c'è automazione COM, e non c'è un collo di bottiglia per processo che serializza le richieste sul server. Ogni richiesta può possedere il proprio oggetto cartella di lavoro, eseguire la riscrittura del calcolo vista nella prima sezione, ed esportare in streaming in parallelo alle altre. La memoria è l'unica risorsa da tenere d'occhio. Il modello della cartella di lavoro vive in RAM per tutta la durata dell'esportazione, quindi un servizio che apre file molto grandi solo per riemetterli come CSV dovrebbe limitare i job concorrenti, o accodare quelli sovradimensionati, invece di lasciare che un picco di traffico decida il working set
Un'impostazione minore: attiva IncludeBOM nelle opzioni HTML quando il frammento verrà salvato come file autonomo che qualche strumento a valle analizza per rilevare la codifica. Quando servi l'HTML direttamente via HTTP, lascia invece che sia l'intestazione della risposta a dichiarare il charset
Quando i byte escono comunque sbagliati
La domanda di supporto più comune sull'esportazione CSV è il problema di apertura travestito diversamente: Excel mostra mojibake invece dei caratteri accentati. L'istinto è dare la colpa al writer, ma emette un BOM UTF-8 esattamente per questo motivo, e il file è quasi sempre corretto nel momento in cui lascia il tuo codice. Qualcosa tra quel punto ed Excel ha mangiato il BOM. Un trasferimento FTP in modalità testo, una copia di stream che salta i primi tre byte, un proxy che ricodifica lungo il percorso: uno qualsiasi di questi rimuoverà il marcatore e lascerà Excel a indovinare la codifica, cosa che fa male. Diagnostica questo al confine, non nella chiamata di esportazione. Apri il file consegnato in un visualizzatore esadecimale e conferma che EF BB BF sia ancora la prima cosa presente
Questo è il filo conduttore per tutti e quattro i formati. La chiamata di esportazione è la parte facile, e HotXLS fa una scelta difendibile a ogni decisione che il writer deve affrontare. I fallimenti vivono alle giunture, dove il testo di una formula incontra un parser che si aspettava un numero, dove un BOM incontra un trasporto che non lo preserva, dove una cella unita incontra il modello di tabella piatto dell'RTF. Ciascuno di questi è un fatto da scrivere nel contratto tra il tuo esportatore e chiunque lo consumi, perché il consumatore non può leggere le tue intenzioni dai byte. Per l'elenco completo dei metodi su entrambe le facciate della cartella di lavoro, la pagina prodotto HotXLS Delphi Component riporta il riferimento completo