Imaginează-ți un job de noapte care construiește în cod un registru de lucru cu facturi și îl scrie ca CSV, ca să fie importat de un sistem din aval. Numerele arată corect în Excel. CSV-ul se deschide curat într-un editor de text. Apoi importatorul se îneacă la coloana de totaluri, pentru că în câmpul de sumă de pe rândul 42 scrie =SUM(D2:D41), adică formula ca text literal, nu cifra la care ar trebui să se calculeze. Nu s-a stricat nimic. Este un comportament documentat și este primul lucru de înțeles despre exportul din HotXLS: scriitorul serializează modelul de celule exact așa cum stă, iar o celulă cu formulă a cărei valoare nu a fost niciodată calculată nu are de predat decât textul formulei
De ce CSV-ul tău conține formule în loc de numere
HotXLS stochează textul formulei și valoarea calculată ca două lucruri separate. SaveAsCSV nu rulează motorul de calcul la ieșire, și asta prin proiectare: un export nu ar trebui să modifice registrul de lucru și nu ar trebui să riște să se împotmolească pe un lanț patologic de formule. Fișierele salvate chiar de Excel poartă rezultate memorate în cache lângă formule, așa că reexportarea lor se comportă exact cum te aștepți. Capcana este specifică registrelor de lucru generate de propriul tău cod, în care formulele au fost scrise, dar niciodată evaluate. Remediul este să faci ca valorile să existe înainte de export, folosind același motor Calculate care rezolvă referințele între foi și funcțiile personalizate:
var
Book: TXLSXWorkbook;
Sheet: TXLSXWorksheet;
R: Integer;
begin
Book := TXLSXWorkbook.Create;
try
Book.Open('invoice-run.xlsx');
Sheet := Book.Sheets[0];
// Materializează rezultatele formulelor, ca CSV-ul să poarte numere, nu 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, ','); // foaia 0, virgulă
Book.SaveAsCSV('feed.tsv', 0, #9); // aceeași foaie, ca TSV
finally
Book.Free;
end;
end;
Uită-te la ce face de fapt bucla: suprascrie celulele cu formule cu valorile lor calculate. Este exact ce trebuie pentru o trecere de export de unică folosință și este greșit dacă vrei să salvezi registrul de lucru din nou ca .xlsx după aceea, pentru că tocmai ai înlocuit formule vii cu numere înghețate. Exportă dintr-o copie sau limitează scrierea înapoi, ca să atingă doar rularea de export. Motorul din spatele lui Calculate merge mai departe de atât, inclusiv cu înregistrarea propriilor tale funcții, ceea ce face subiectul articolului despre motorul de formule HotXLS și funcțiile personalizate
Ce garantează scriitorul delimitat
Calea CSV produce UTF-8 cu marcaj de ordine a octeților, terminații de linie CRLF și punere între ghilimele conform RFC 4180. Orice câmp care conține delimitatorul, o ghilimea sau o întrerupere de linie este împachetat, iar ghilimelele dinăuntru sunt dublate. Datele calendaristice se redau ca yyyy-mm-dd hh:nn:ss, indiferent de formatul de afișare al celulei. Este alegerea corectă pentru un consumator de tip mașină, deși îl surprinde pe oricine se aștepta ca formatarea de pe ecran să fie dusă mai departe. Celulele cu text îmbogățit sunt aplatizate prin concatenarea segmentelor lor
Valorile acestea implicite rezolvă majoritatea disputelor cu un importator înainte ca ele să înceapă, dar două dintre ele își au oricum locul în contractul tău de interfață. Prima este BOM-ul. El este cel care îi permite Excel să deschidă fișierul cu caracterele cu diacritice intacte, însă câteva parsere stricte tratează acei trei octeți drept date; dacă al tău este dintre ele, elimină-i la predare. A doua este TSV. Nu este deloc o funcționalitate separată, ci același scriitor apelat cu #9 pe post de delimitator, așa că tot ce s-a spus mai sus i se aplică neschimbat. Foaia de exportat se alege prin index numerotat de la 0 în supraîncărcarea cu mai multe argumente, în timp ce prescurtarea SaveAsCSV(FileName) cu un singur argument ia foaia activă
Exportul HTML este un instantaneu, nu un format de interschimb
Acolo unde CSV aruncă tot în afară de valori, SaveAsHTML încearcă să păstreze aspectul: câte un <table> pentru fiecare foaie, regiunile îmbinate exprimate ca colspan și rowspan, stilizare de bază a celulelor turnată direct ca CSS. Culorile raportate la temă sunt sărite, nu rezolvate, așa că un șablon care se sprijină pe sloturi de temă iese mai sărăcăcios decât arată în Excel. Setează culori RGB explicite pe tot ce trebuie să supraviețuiască drumului. Obiectul de opțiuni controlează anvelopa:
var
Opts: TXLSXHtmlExportOptions;
begin
Opts := TXLSXHtmlExportOptions.Create;
try
Opts.Title := 'Weekly settlement';
Opts.TableClass := 'report-grid'; // cârlig pentru foaia de stil a paginii gazdă
Opts.WriteDocument := True; // pagină întreagă, nu un fragment
if Book.SaveAsHTML('settlement.html', 0, Opts) <> 0 then
raise Exception.Create('Sheet index out of range');
finally
Opts.Free;
end;
end;
Două detalii din fragmentul acela merită atenția. Comută WriteDocument pe False și rezultatul devine un fragment de tabel simplu în loc de o pagină întreagă, ceea ce vrei atunci când injectezi o previzualizare într-un aspect existent: setează TableClass și lasă foaia de stil a gazdei să se ocupe de tematizare. Și convenția de returnare este inversă față de majoritatea apelurilor HotXLS. SaveAsHTML întoarce 0 la succes și -1 pentru un index de foaie greșit, așa că o verificare făcută din obișnuință pentru = 1 va raporta fiecare export reușit drept eșec. Când ai nevoie de o regiune, nu de o foaie întreagă, poate ca să o trimiți pe e-mail sau să o încorporezi ca bloc unic, TXLSXRange.SaveAsHTML exportă orice interval dreptunghiular după aceleași reguli de randare
Ieșirea RTF și locul pe care încă și-l merită
A patra țintă scrie tabele RTF 1.6, câte o foaie la fiecare apel prin SaveAsRTF. Lățimile de coloană sunt aproximate la circa 96 de twips pentru fiecare caracter de lățime de coloană. Limitarea structurală de știut este că celulele îmbinate nu se întind în rezultat: doar celula-ancoră își poartă conținutul, iar celulele acoperite ies goale. Asta scoate RTF din discuție pentru șabloanele cu aspect încărcat. Își merită totuși locul ca drum al celei mai mici rezistențe când vrei să arunci rezultate tabelare într-un procesor de text sau într-un sistem vechi de gestiune a documentelor, de dinaintea ingestiei de HTML
Dus-întorsul: importul CSV este distructiv prin proiectare
Citirea CSV înapoi are propriul contract. OpenCSV golește tot registrul de lucru și îl reconstruiește ca o singură foaie numită Sheet1. Este un constructor în spirit, nu o îmbinare, așa că nu îl apela niciodată pe un registru de lucru care încă ține conținut nesalvat. Trecerea lui #0 ca separator declanșează detectarea automată a delimitatorului. Indicatorul ADetectTypes controlează promovarea de tip: cu el pornit, șirurile numerice devin numere, șirurile ISO-8601 devin date calendaristice, iar true/false devin valori booleene. Oprește-l când fluxul poartă identificatori cu zerouri în față, coduri poștale sau coduri de produs, pe care promovarea le stâlcește tăcut, transformându-le în numere (un zero din față pur și simplu dispare în clipa în care 00123 devine 123). Ambele fațade expun același import. Împerechează-l cu apelurile de export de mai sus și ai o punte între formate care nu are nevoie de Excel instalat nicăieri în pipeline, scenariul tratat în generarea de rapoarte din baza de date în Excel cu HotXLS
Export direct într-un flux
Fiecare scriitor de aici are, lângă versiunea cu nume de fișier, o supraîncărcare pe flux: CSV, HTML, RTF și formatele de registru de lucru propriu-zise. În codul de server, supraîncărcările acelea sunt cele la care să apelezi. Un endpoint web care servește o descărcare CSV poate scrie într-un TMemoryStream și îl poate preda direct obiectului de răspuns, fără fișier temporar, fără job de curățenie și fără coliziune între două cereri care s-au nimerit să aleagă același nume generat. Același lucru este valabil pentru împingerea exporturilor în stocare de tip blob sau pentru atașarea lor la e-mailuri de ieșire. Sistemul de fișiere iese complet din peisaj
Tiparul acesta se compune cu felul în care se instalează biblioteca. Ambele fațade sunt cititoare și scriitoare native Object Pascal, așa că nu există instalare de Excel, nu există automatizare COM și nu există un gât de sticlă per proces care să serializeze cererile pe server. Fiecare cerere își poate deține propriul obiect registru de lucru, poate rula scrierea înapoi a calculelor din prima secțiune și își poate transmite exportul în paralel cu ale vecinelor. Memoria este singura resursă de ținut sub ochi. Modelul registrului de lucru stă în RAM pe toată durata exportului, așa că un serviciu care deschide fișiere foarte mari doar ca să le reemită drept CSV ar trebui să plafoneze joburile concurente sau să le pună la coadă pe cele supradimensionate, în loc să lase un vârf de trafic să hotărască setul de lucru
Încă un buton mai mic: setează IncludeBOM pe opțiunile HTML atunci când fragmentul va fi salvat ca fișier de sine stătător, pe care vreun instrument din aval îl adulmecă pentru codificare. Când servești HTML direct peste HTTP, lasă declarația setului de caractere în seama antetelor de răspuns
Când octeții tot ies greșit
Cea mai frecventă întrebare de suport despre exportul CSV este problema din deschidere într-un alt costum: Excel arată caractere aiurea în loc de litere cu diacritice. Instinctul este să dai vina pe scriitor, dar el emite un BOM UTF-8 exact din motivul acesta, iar fișierul este aproape întotdeauna corect când pleacă din codul tău. Ceva dintre punctul acela și Excel a mâncat BOM-ul. Un transfer FTP în mod text, o copiere de flux care sare peste primii trei octeți, un proxy care recodifică pe drum: oricare dintre ele va îndepărta marcajul și îl va lăsa pe Excel să ghicească codificarea, lucru pe care îl face prost. Diagnostichează asta la graniță, nu în apelul de export. Deschide fișierul livrat într-un vizualizator hexazecimal și confirmă faptul că EF BB BF este încă primul lucru din el
Aceasta este linia care străbate toate cele patru formate. Apelul de export este partea ușoară, iar HotXLS face o alegere care se poate apăra la fiecare decizie cu care se confruntă scriitorul. Eșecurile trăiesc la cusături, acolo unde textul unei formule dă peste un parser care voia un număr, unde un BOM dă peste un transport care nu îl păstrează, unde o celulă îmbinată dă peste modelul de tabel plat al RTF. Fiecare dintre acestea este un fapt de scris în contractul dintre exportatorul tău și orice îl consumă, pentru că cel care consumă nu îți poate citi intențiile din octeți. Pentru lista completă de metode din ambele fațade de registru de lucru, pagina de produs HotXLS Delphi Component poartă referința integrală