Műszaki cikk

CSV, TSV és HTML export Delphiben a HotXLS-szel

Képzeljen el egy éjszakai feladatot, amely kódból felépít egy számlázási munkafüzetet, majd CSV-ként írja ki egy alsóbb rendszer számára. A számok Excelben rendben látszanak. A CSV tisztán megnyílik egy szövegszerkesztőben. Aztán az importáló megakad az összegoszlopon, mert a 42. sor összegmezőjében a =SUM(D2:D41) áll, vagyis a képlet szó szerinti szövegként, nem az az érték, amelyre ki kellene számolódnia. Semmi nem hibás. Ez dokumentált viselkedés, és ez az első dolog, amit a HotXLS exportjáról tudni kell: az író pontosan úgy sorosítja a cellamodellt, ahogyan az áll, egy képletcella pedig, amelynek értékét soha nem számolták ki, csak a képletszövegét tudja átadni

Miért képletek vannak a CSV-ben a számok helyett

A HotXLS a képletszöveget és a kiszámolt értéket két külön dologként tárolja. A SaveAsCSV szándékosan nem futtatja a számolómotort kifelé menet: egy exportnak nem szabad módosítania a munkafüzetet, és nem szabad megkockáztatnia, hogy egy kóros képletláncon elakad. Azok a fájlok, amelyeket maga az Excel mentett, a képletek mellett gyorsítótárazott eredményeket is hordoznak, így ezek újraexportálása a várt módon viselkedik. A csapda kifejezetten a saját kódja által előállított munkafüzetekre jellemző, ahol a képleteket megírták, de sosem értékelték ki. A megoldás az, hogy az export előtt létező értékeket csinál belőlük ugyanazzal a Calculate motorral, amely a lapok közötti hivatkozásokat és az egyéni függvényeket is feloldja:

Ábra, amely egy Delphi HotXLS munkafüzet celláját mutatja, amely csak képletszöveget tart, amíg a Book.Calculate ki nem számolja az értéket, így a CSV export számot ad ki az =SUM szöveg helyett
A SaveAsCSV úgy sorosítja a cellamodellt, ahogyan az áll — Calculate nélkül az összegmező szó szerinti képletszöveget hordoz, és az importáló elutasítja
var
  Book: TXLSXWorkbook;
  Sheet: TXLSXWorksheet;
  R: Integer;
begin
  Book := TXLSXWorkbook.Create;
  try
    Book.Open('invoice-run.xlsx');
    Sheet := Book.Sheets[0];

    // A képleteredmények anyagiasítása, hogy a CSV számokat vigyen, ne '=...' szöveget
    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, ',');    // 0. lap, vessző
    Book.SaveAsCSV('feed.tsv', 0, #9);     // ugyanaz a lap TSV-ként
  finally
    Book.Free;
  end;
end;

Figyelje meg, mit tesz valójában a ciklus: felülírja a képletcellákat a kiszámolt értékükkel. Ez pontosan helyes egy eldobható exportmenethez, és helytelen akkor, ha utána újra .xlsx formátumban akarja menteni a munkafüzetet, mert az imént cserélte le az élő képleteket befagyasztott számokra. Másolatból exportáljon, vagy szűkítse úgy a visszaírást, hogy csak az exportfutásra hasson. A Calculate mögötti motor ennél tovább megy, például saját függvények regisztrálását is engedi, ami a HotXLS képletmotorja és az egyéni függvények témája

Mit garantál a tagolt formátumot író egység

A CSV útvonal bájtsorrend-jelzővel ellátott UTF-8 kimenetet, CRLF sorvégeket és RFC 4180 szerinti idézőjelezést állít elő. Minden olyan mező, amely az elválasztót, idézőjelet vagy sortörést tartalmaz, körbekerül, a beágyazott idézőjelek pedig megkettőződnek. A dátumok yyyy-mm-dd hh:nn:ss alakban jelennek meg, függetlenül a cella megjelenítési formátumától. Gépi fogyasztó esetén ez a helyes döntés, még ha meg is lepi azt, aki a képernyőn látott formázás átöröklésére számított. A rich text cellák a futamaik összefűzésével laposodnak ki

Ábra az egyetlen HotXLS tagolt írót ábrázolva Delphiben, amely vesszővel CSV-t, #9 karakterrel TSV-t állít elő, miközben mindkét kimenet UTF-8 BOM-ot, CRLF sorvégeket és RFC 4180 idézőjelezést oszt meg
A CSV és a TSV ugyanabból az íróból származik, így az UTF-8 BOM, a CRLF sorvégek és az RFC 4180 idézőjelezés változatlanul mindkettőre érvényes

Ezek az alapértelmezések a legtöbb vitát még kirobbanása előtt eldöntik az importálóval, kettő közülük azonban mindenképpen a felületi szerződésbe való. Az első a BOM. Ez teszi lehetővé, hogy az Excel ép ékezetes karakterekkel nyissa meg a fájlt, néhány szigorú elemző viszont adatként kezeli ezt a három bájtot; ha az Öné ilyen, vágja le őket az átadásnál. A második a TSV. Ez egyáltalán nem külön képesség, csupán ugyanaz az író #9 elválasztóval hívva, így minden fenti változatlanul érvényes rá. Az exportálandó lapot a többargumentumos túlterhelésben 0-alapú index választja ki, míg az egyargumentumos SaveAsCSV(FileName) rövidítés az aktív lapot veszi

A HTML export pillanatfelvétel, nem csereformátum

Ahol a CSV az értékeken kívül mindent eldob, ott a SaveAsHTML igyekszik megőrizni a megjelenést: laponként egy <table>, az egyesített tartományok colspan és rowspan alakban, az alapvető cellastílusok pedig beágyazott CSS-ként. A témához viszonyított színeket kihagyja, nem oldja fel, így egy témahelyekre támaszkodó sablon jóval csupaszabban jön ki, mint amilyennek Excelben látszik. Állítson be kifejezett RGB színeket mindenre, aminek túl kell élnie az utat. A borítékot a beállításobjektum vezérli:

var
  Opts: TXLSXHtmlExportOptions;
begin
  Opts := TXLSXHtmlExportOptions.Create;
  try
    Opts.Title := 'Weekly settlement';
    Opts.TableClass := 'report-grid';     // kapaszkodó a befogadó oldal stíluslapjához
    Opts.WriteDocument := True;           // teljes oldal, nem töredék
    if Book.SaveAsHTML('settlement.html', 0, Opts) <> 0 then
      raise Exception.Create('Sheet index out of range');
  finally
    Opts.Free;
  end;
end;

Két részlet ebben a részletben megéri a figyelmet. Állítsa a WriteDocument értékét False értékre, és a kimenet teljes oldal helyett puszta táblázattöredék lesz, ami akkor kell, amikor előnézetet szúr be egy meglévő elrendezésbe: adja meg a TableClass értékét, és bízza a témázást a befogadó stíluslapra. A visszatérési egyezmény is a legtöbb HotXLS hívás fordítottja. A SaveAsHTML sikerkor 0-t, hibás lapindexre -1-et ad vissza, így a megszokásból írt = 1 ellenőrzés minden sikeres exportot kudarcként jelent. Amikor teljes lap helyett tartományra van szüksége, például egyetlen blokk küldéséhez vagy beágyazásához, a TXLSXRange.SaveAsHTML bármely téglalap alakú tartományt ugyanezen megjelenítési szabályok szerint exportál

Az RTF kimenet és ahol még mindig megállja a helyét

A negyedik célformátum RTF 1.6 táblázatokat ír, hívásonként egy lapot a SaveAsRTF segítségével. Az oszlopszélességeket nagyjából 96 twip / oszlopszélesség-karakter arányban közelíti. A tudnivaló szerkezeti korlát az, hogy az egyesített cellák nem nyúlnak át a kimenetben: csak a horgonycella hordozza a tartalmát, a lefedett cellák üresként kerülnek ki. Ez kizárja az RTF-et az elrendezésre építő sablonoknál. Továbbra is megállja a helyét mint a legkisebb ellenállás útja ahhoz, hogy táblázatos eredményeket ejtsen egy szövegszerkesztőbe vagy egy olyan örökölt dokumentumkezelő rendszerbe, amely megelőzi a HTML beolvasását

Oda-vissza út: a CSV importja szándékosan romboló

A CSV visszaolvasásának megvan a maga szerződése. Az OpenCSV kiüríti az egész munkafüzetet, és Sheet1 nevű egyetlen lapként építi újra. Szellemében konstruktor, nem összefésülés, ezért soha ne hívja meg olyan munkafüzeten, amely még mentetlen tartalmat őriz. A #0 elválasztóként átadva automatikus elválasztófelismerést indít. Az ADetectTypes jelző a típusléptetést vezérli: bekapcsolva a numerikus szövegekből számok, az ISO-8601 szövegekből dátumok, a true/false értékekből logikai értékek lesznek. Kapcsolja ki, amikor az adatfolyam vezető nullás azonosítókat, irányítószámokat vagy termékkódokat hoz, mert a léptetés mindet némán számokká gyűri (a vezető nulla egyszerűen eltűnik abban a pillanatban, amikor a 00123 értékből 123 lesz). Mindkét homlokzat ugyanezt az importot teszi elérhetővé. Párosítsa a fenti exporthívásokkal, és olyan formátumhidat kap, amelyhez a folyamat egyetlen pontján sem kell telepített Excel, ez pedig az adatbázisból Excelbe készülő jelentések a HotXLS-szel témában tárgyalt forgatókönyv

Export egyenesen adatfolyamba

Itt minden írónak van adatfolyamos túlterhelése a fájlnevet váró változat mellett: a CSV, a HTML, az RTF és maguk a munkafüzet-formátumok is. Kiszolgálókódban ezek a túlterhelések a helyes választás. Egy CSV letöltést kiszolgáló webes végpont TMemoryStream objektumba írhat, és azt adhatja egyenesen a válaszobjektumnak, ideiglenes fájl, takarítófeladat és két olyan kérés közötti ütközés nélkül, amely véletlenül ugyanazt az előállított nevet választotta. Ugyanez áll az exportok blobtárolóba tolására vagy kimenő levélhez csatolására. A fájlrendszer teljesen kiesik a képből

Ez a minta összeadódik azzal, ahogyan a könyvtár települ. Mindkét homlokzat natív Object Pascal olvasó és író, így nincs Excel-telepítés, nincs COM automatizálás, és nincs olyan folyamatonkénti szűk keresztmetszet, amely sorba állítaná a kéréseket a kiszolgálón. Minden kérés birtokolhatja a saját munkafüzet-objektumát, lefuttathatja az első szakaszból ismert számolásos visszaírást, és a szomszédaival párhuzamosan folyamba küldheti az exportját. A memória az az egy erőforrás, amelyet szemmel kell tartani. A munkafüzet-modell az export teljes idejére a memóriában él, ezért annak a szolgáltatásnak, amely nagyon nagy fájlokat pusztán CSV-ként való újrakibocsátásukhoz nyit meg, korlátoznia kell az egyidejű feladatokat, vagy sorba állítania a túlméretezetteket, ahelyett hogy egy forgalmi csúcsra bízná a munkakészlet méretét

Egy kisebb szabályzó: állítsa be az IncludeBOM értékét a HTML beállításokon, amikor a töredéket önálló fájlként mentik, és valamely alsóbb eszköz szimatolja belőle a kódolást. Amikor HTML-t közvetlenül HTTP fölött szolgál ki, hagyja a karakterkészlet megadását a válaszfejlécekre

Amikor a bájtok mégis rosszul jönnek ki

A CSV exporttal kapcsolatos leggyakoribb támogatási kérdés a nyitó gond más jelmezben: az Excel ékezetes karakterek helyett kacatot mutat. Az ösztön az írót hibáztatja, pedig az pontosan ezért ad ki UTF-8 BOM-ot, és a fájl szinte mindig helyes, amikor elhagyja a kódját. Onnantól az Excelig valami megette a BOM-ot. Egy szöveges módú FTP átvitel, egy adatfolyam-másolás, amely átugorja az első három bájtot, egy átjáró, amely útközben újrakódol: bármelyik letépi a jelzőt, és az Excelre hagyja a kódolás kitalálását, amit rosszul csinál. Ezt a határon diagnosztizálja, ne az exporthívásban. Nyissa meg a leszállított fájlt hexnézőben, és igazolja, hogy az EF BB BF még mindig az első dolog benne

Ábra arról, hogyan tépi le a HotXLS CSV exportja által Delphiben helyesen kiírt UTF-8 BOM jelzőt egy szöveges módú FTP átvitel vagy egy újrakódoló átjáró, ezzel kacatot mutattatva az Excellel
Az író helyesen bocsátja ki az EF BB BF jelzőt — a kacat csak azután jelenik meg, hogy egy átviteli lépés letépte, ezért a leszállított bájtokat vizsgálja hexnézőben

Ez a közös vonal mind a négy formátumban. Az exporthívás a könnyű rész, és a HotXLS minden döntésnél védhető választást tesz, amellyel az író szembekerül. A hibák a varratoknál élnek, ahol a képletszöveg olyan elemzővel találkozik, amely számot várt, ahol a BOM olyan átvitellel találkozik, amely nem őrzi meg, ahol az egyesített cella az RTF lapos táblázatmodelljével találkozik. Mindegyik olyan tény, amelyet bele kell írni az exportálója és a fogyasztója közötti szerződésbe, mert a fogyasztó nem tudja kiolvasni a szándékait a bájtokból. A mindkét munkafüzet-homlokzatra kiterjedő teljes metóduslistát a HotXLS Delphi Component termékoldala hordozza teljes hivatkozásként