Tehnični članak

Exporting Excel Workbooks to CSV, TSV, HTML, and RTF from Delphi with HotXLS

Predstavljajte si nočno opravilo, ki v kodi zgradi delovni zvezek za račune in ga izpiše kot CSV za uvoz v sistem v nadaljevanju. Številke so v Excelu videti pravilne. CSV se čisto odpre v urejevalniku besedila. Nato pa se uvoznik zatakne pri stolpcu s skupnimi vsotami, saj polje za znesek v 42. vrstici vsebuje besedilo =SUM(D2:D41) — formulo kot dobesedno besedilo in ne kot številko, v katero bi se morala izračunati. Nič ni pokvarjeno. To je dokumentirano obnašanje in je prva stvar, ki jo je treba razumeti pri izvozu iz HotXLS: pisalnik serializira model celice natanko takšen, kot je, celica s formulo, katere vrednost ni bila nikoli izračunana, pa lahko preda le svoje besedilo formule

Zakaj vaš CSV vsebuje formule namesto številk

HotXLS shranjuje besedilo formule in izračunano vrednost kot dve ločeni stvari. Metoda SaveAsCSV po zasnovi med izvozom ne zažene računskega mehanizma: izvoz ne bi smel spreminjati delovnega zvezka in ne bi smel tvegati zastoja pri patološki verigi formul. Datoteke, ki jih je shranil Excel sam, nosijo predpomnjene rezultate poleg formul, zato se ponovni izvoz teh obnaša tako, kot pričakujete. Past je specifična za delovne zvezke, ki jih je ustvarila vaša koda, kjer so bile formule zapisane, a nikoli ovrednotene. Rešitev je v tem, da poskrbite, da vrednosti obstajajo pred izvozom, z uporabo istega mehanizma Calculate, ki razrešuje medlistne reference in funkcije po meri:

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;

Poglejte, kaj zanka dejansko počne: prepiše celice s formulami z njihovimi izračunanimi vrednostmi. To je povsem pravilno za enkraten izvozni prehod in napačno, če nameravate delovni zvezek kasneje znova shraniti kot .xlsx, saj ste pravkar zamenjali žive formule z zamrznjenimi številkami. Izvozite iz kopije ali pa omejite ponovni zapis tako, da vpliva le na izvozni tek. Mehanizem za funkcijo Calculate gre še dlje od tega, vključno z registracijo lastnih funkcij, kar je tema članka o mehanizmu za formule HotXLS in funkcijah po meri

Kaj jamči pisalnik z ločili

Pot izvoza v CSV ustvari datoteko UTF-8 z oznako zaporedja bajtov (BOM), zaključek vrstic CRLF in navednice po RFC 4180. Vsako polje, ki vsebuje ločilo, narekovaj ali prelom vrstice, se zaobjame v narekovaje, vgrajeni narekovaji pa se podvojijo. Datumi se izrišejo v obliki yyyy-mm-dd hh:nn:ss, ne glede na obliko prikaza celice. To je pravilna odločitev za strojnega porabnika, čeprav preseneča vsakogar, ki je pričakoval prenos oblikovanja z zaslona. Celice z bogatim besedilom (rich text) se sploščijo z združevanjem njihovih delov

Te privzete nastavitve rešijo večino sporov z uvoznikom še pred začetkom, vendar dve izmed njih vseeno sodita v vašo pogodbo o vmesniku. Prva je oznaka BOM. Ta omogoča Excelu, da odpre datoteko z nedotaknjenimi šumniki in naglašenimi znaki, vendar nekaj strogih razčlenjevalnikov te tri bajte obravnava kot podatke; če je vaš med njimi, jih odstranite ob predaji. Druga je TSV. To sploh ni ločena funkcija, temveč le isti pisalnik, poklican z ločilom #9, zato vse zgoraj navedeno velja zanjo nespremenjeno. List za izvoz se izbere z z 0 indeksiranim indeksom v preobremenitvi z več argumenti, medtem ko krajša oblika z enim argumentom SaveAsCSV(FileName) vzame aktivni list

Izvoz v HTML je posnetek stanja in ne format za izmenjavo

Kjer CSV zavrže vse razen vrednosti, poskuša SaveAsHTML ohraniti videz: ena <table> na list, združena območja, izražena kot colspan in rowspan, osnovno oblikovanje celic pa je vključeno kot CSS. Barve, povezane s temo, se preskočijo in ne razrešijo, zato je predloga, ki se opira na reže tem, videti preprostejša kot v Excelu. Nastavite eksplicitne barve RGB na vsem, kar mora preživeti to pot. Predmet možnosti nadzoruje ovojnico:

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;

Dve podrobnosti v tem izseku si zaslužita pozornost. Nastavite WriteDocument na False in izhod postane goli fragment tabele namesto celotne strani, kar je tisto, kar želite pri vbrizgavanju predogleda v obstoječo postavitev: nastavite TableClass in prepustite oblikovanje slogovni predlogi gostitelja. Pravilo vračanja je prav tako obratno od večine klicev v HotXLS. Metoda SaveAsHTML ob uspehu vrne 0 in -1 za napačen indeks lista, zato bo preverjanje = 1 vsak uspešen izvoz označilo kot napako. Ko potrebujete območje namesto celotnega lista, morda za pošiljanje po e-pošti ali vgradnjo posameznega bloka, klic TXLSXRange.SaveAsHTML izvozi kateri koli pravokotni obseg po enakih pravilih izrisa

Izhod RTF in kje še vedno najde svoje mesto

Četrti cilj prek metode SaveAsRTF zapiše tabele RTF 1.6, en list na klic. Širine stolpcev so približane na približno 96 twipov na znak širine stolpca. Strukturna omejitev, ki jo je treba poznati, je ta, da se združene celice v izhodu ne raztezajo: le sidrna celica nosi svojo vsebino, pokrite celice pa se izpišejo kot prazne. To izključuje RTF za predloge z zahtevno postavitvijo. Svoje mesto pa še vedno najde kot pot najmanjšega upora za prenos tabelarnih rezultatov v urejevalnik besedil ali v zapuščinski sistem za upravljanje dokumentov, ki ne podpira uvoza HTML

Krožno potovanje: uvoz CSV je po zasnovi destruktiven

Branje CSV nazaj ima svojo pogodbo. Klic OpenCSV počisti celoten delovni zvezek in ga znova zgradi kot en sam list z imenom Sheet1. V svojem bistvu je to konstruktor in ne združevanje, zato ga nikoli ne kličite na delovnem zvezku, ki še vsebuje neshranjeno vsebino. Posredovanje znaka #0 ako ločila sproži samodejno zaznavanje ločil. Zastavica ADetectTypes nadzoruje pretvorbo tipov: ko je vklopljena, numerični nizi postanejo številke, nizi ISO-8601 postanejo datumi, vrednosti true/false pa logične vrednosti. Izklopite jo, ko vir podatkov vsebuje identifikatorje z vodilnimi ničlami, poštne številke ali kode izdelkov, saj bi jih pretvorba tiho spremenila v številke (vodilna ničla preprosto izgine v trenutku, ko 00123 postaje 123). Obe fasadi izpostavljata enak uvoz. Združite ga z zgornjimi klici izvoza in dobili boste formatni most, ki ne potrebuje nameščenega Excela nikjer v verigi, kar je scenarij, obravnavan v članku o generiranju poročil iz baze podatkov v Excel s HotXLS

Izvoz neposredno v tok (stream)

Vsak pisalnik tukaj ima poleg različice z imenom datoteke tudi preobremenitev s tokom: CSV, HTML, RTF in sami formati delovnih zvezkov. V strežniški kodi so te preobremenitve tiste, po katerih velja poseči. Spletna končna točka, ki ponuja prenos CSV, lahko piše v TMemoryStream in ga preda neposredno odzivnemu objektu, brez začasne datoteke, brez čiščenja in brez trka med dvema zahtevama, ki bi naključno izbrali isto generirano ime. Enako velja za potiskanje izvozov v shrambo blob ali pripenjanje k odhodni pošti. Datotečni sistem v celoti izgine iz slike

Ta vzorec se ujema s tem, kako se knjižnica namesti. Obe fasadi sta domača pisalnika in bralnika v Object Pascalu, zato ni nameščanja Excela, ni avtomatizacije COM in ni ozkega grla na ravni procesa, ki bi serializiralo zahteve na strežniku. Vsaka zahteva ima lahko lasten objekt delovnega zvezka, zažene računski ponovni zapis iz prvega poglavja in pretaka svoj izvoz vzporedno s sosednjimi. Pomnilnik je edini vir, na katerega je treba popaziti. Model delovnega zvezka med izvozom živi v pomnilniku RAM, zato bi morala storitev, ki odpira zelo velike datoteke le zato, da jih ponovno izda kot CSV, omejiti število sočasnih opravil ali pa prevelika opravila postaviti v vrsto, namesto da bi prometna konica določala delovni nabor pomnilnika

Ena manjša nastavitev: nastavite IncludeBOM v možnostih HTML, ko bo fragment shranjen kot samostojna datoteka, pri kateri orodje v nadaljevanju preverja kodiranje. Ko HTML strežete neposredno prek HTTP, raje prepustite deklaracijo nabora znakov (charset) glavam odziva

Ko so bajti še vedno napačni

Najbolj pogosto vprašanje za podporo glede izvoza v CSV je težava z odpiranjem v drugi preobleki: Excel namesto šumnikov in naglašenih znakov prikaže popačeno besedilo (mojibake). Instinkt je okriviti pisalnik, vendar ta prav iz tega razloga izda UTF-8 BOM, datoteka pa je skoraj vedno pravilna, ko zapusti vašo kodo. Nekaj med kodo in Excelom je pojedlo oznako BOM. Prenos FTP v besedilnem načinu, kopiranje toka, ki preskoči prve tri bajte, posredniški strežnik (proxy), ki med potjo spremeni kodiranje: karkoli od tega bo odstranilo oznako in pustil Excelu, da ugiba o kodiranju, kar pa počne slabo. To diagnosticirajte na meji in ne v samem klicu izvoza. Odprite prejeto datoteko v heksagonalnem pregledovalniku in potrdite, da je EF BB BF še vedno prva stvar v njej

To velja za vse štiri formate. Klic izvoza je preprost del, HotXLS pa sprejme razumno odločitev pri vsaki izbiri, s katero se pisalnik sooči. Napake živijo na stikih, kjer se besedilo formule sreča z razčlenjevalnikom, ki je želel številko, kjer se oznaka BOM sreča s prenosom, ki je ne ohrani, ali kjer se združena celica sreča z RTF-ovim modelom ravne tabele. Vsako od teh dejstev je treba zapisati v pogodbo med vašim izvoznikom in tistim, kar ga porablja, saj porabnik ne more prebrati vaših namer iz samih bajtov. Za celoten seznam metod na obeh fasadah delovnih zvezkov stran izdelka HotXLS Component vsebuje celotno referenco