Tekninen artikkeli

Taulukoiden nimien nopea listaus Delphissä HotXLS GetSheetNames -metodilla

Joskus ainoa vastaanottorutiinin tarvitsema vastaus on rakenteellinen: onko tässä työkirjassa taulukko nimeltä "Mapping", tai kuinka monta välilehteä siinä on. Siihen vastaaminen Open-metodia kutsumalla on kallis tapa tehdä se. Täysi avaus paisuttaa jaettujen merkkijonojen taulukon, purkaa jokaisen tyylitietueen ja käy läpi jokaisen laskentataulukon solut, koska se ei voi tietää, että halusit vain sisällysluettelon. Suuressa tiedostossa tämä tarkoittaa satoja megatavuja varauksia ja useita sekunteja suoritinaikaa muutaman kilotavun kokoisen luettelon lukemiseen. HotXLS, losLabin natiivi Delphin laskentataulukkokirjasto, antaa tämän luettelon erikseen: GetSheetNames palauttaa laskentataulukoiden nimet työkirjan järjestyksessä muodostamatta yhtäkään solua

Miksi luettelo on halpa lukea

Molemmat laskentataulukkomuodot sijoittavat sisällysluettelonsa lähelle alkua, minkä ansiosta luettelointikutsu on nopea eikä vain nokkela. OOXML-paketti säilyttää taulukkoluettelon osassa xl/workbook.xml, joka pysyy pienenä riippumatta siitä, onko työkirjassa kymmenen vai kymmenen miljoonaa riviä. BIFF8 .xls tallentaa BoundSheet-tietueensa työkirjan globaalin tietovirran alkuun ennen solutietoja. Luettelointikutsun välttämä työ ei siis ole pyöristysvirhe verrattuna täyteen avaukseen. Se on suurin osa tiedostosta. Luettelon lukeminen maksaa saman kourallisen kilotavuja rivimäärästä riippumatta, kun taas täysi avaus skaalautuu datan mukana, ja monimegatavuisessa työkirjassa ero kasvaa useisiin kertaluokkiin sekä kosketetuissa tavuissa että varatussa muistissa

HotXLS GetSheetNames Delphissä lukee vain XLSX- tai XLS-tiedoston taulukkoluettelon, kun taas täysi avaus kulkee jokaisen solun läpi
Katalogi sijaitsee workbook.xml-tiedostossa tai BoundSheet-tietueissa, joten luettelointi maksaa muutaman kilotavun, kun taas täysi avaus skaalautuu datan mukaan

Tämä kiinteä kustannus on ominaisuus, jonka ympärille kannattaa suunnitella. GetSheetNames-metodille rakennettu vastaanottoportti käyttäytyy samoin 200 rivin ja 200 Mt:n tiedostolla, joten erän hitain tiedosto ei enää määrää tahtia päätettäessä, kannattaako tiedostoa edes käsitellä

Yksi kutsu .xls-, .xlsx- ja mallimuodoille

XLS-rajapinnassa TXLSWorkbook.GetSheetNames lukee muutakin kuin .xls-tiedostoja. Se hyväksyy myös zip-pohjaiset .xlsx-, .xlsm-, .xltx- ja .xltm-tiedostot ja poimii arkistosta vain workbook.xml-tiedoston. Aidolle .xls-syötteelle se lukee BoundSheet-tietueet ja pysähtyy globaalin alitietovirran ensimmäiseen EOF-tietueeseen, joten suuri binaaritiedosto maksaa edelleen vain alkuosan kilotavut. XLSX-rajapinnassa on takuu, joka merkitsee pitkäkestoiselle palvelukoodille enemmän kuin ensiksi näyttää: TXLSXWorkbook.GetSheetNames ei nollaa eikä täytä työkirjaesiintymää, joten jo avointa asiakirjaa pitävä esiintymä voi tutkia muita tiedostoja häiritsemättä käsillä olevaa. GetODSSheetNames soveltaa samaa lähestymistapaa OpenDocument-paketteihin, ja jokaisessa näistä kutsuista on tietovirtaylikuormitus, joten voit tarkistaa latauksen, joka ei koskaan päädy levylle

var
  Book: TXLSXWorkbook;
  Names: TStringList;
  I: Integer;
begin
  Names := TStringList.Create;
  Book := TXLSXWorkbook.Create;
  try
    if Book.GetSheetNames('upload-7f3a.xlsx', Names) <= 0 then
      raise Exception.Create('unreadable workbook package');
    if Names.IndexOf('Mapping') < 0 then
      raise Exception.Create('required Mapping sheet is missing');
    for I := 0 to Names.Count - 1 do
      Writeln(Format('sheet %d: %s', [I, Names[I]]));
  finally
    Book.Free;
    Names.Free;
  end;
end;

Sama kutsu toimii hyvin työpöytäsovelluksen tuontivalintaikkunassa. Listaa taulukot, anna käyttäjän valita yksi ja maksa täydestä avauksesta vasta valinnan jälkeen. 50 taulukon työkirjassa ero näkyy: valitsin ilmestyy heti sen sijaan, että se pysähtyisi koko tiedoston latautuessa taustalla

Makroja sisältävät .xlsm-tiedostot ja mallimuodot luetteloituvat täsmälleen kuten tavallinen .xlsx, sillä luettelo on samassa workbook.xml-tiedostossa riippumatta siitä, kulkeeko paketissa mukana vbaProject.bin. Vastaanottoputki voi siksi luetella makrotyökirjan taulukot reititystä varten koskematta koskaan makrojen hyötykuormaan tai tekemättä mitään, mikä suorittaisi sen, ja jättää makrokäytännön päätöksen vaiheeseen, joka todella avaa tiedoston

Paluuarvon lukeminen harhauttamatta itseäsi

Palautuskäytännöt eivät ole HotXLS:ssä yhtenäisiä. Jotkin kutsut palauttavat onnistumisesta arvon 1, toiset määrän, joten luettelointifunktioissa ainoa kestävä tarkistus on käsitellä nolla tai sitä pienempi arvo epäonnistumisena ja tyhjentää merkkijonoluettelo. Vastusta kiusausta tulkita tyhjä luettelo "työkirjaksi ilman taulukoita". Sekä ECMA-376 että BIFF8-määrittely edellyttävät vähintään yhtä taulukkoa kelvollisessa työkirjassa, joten nolla nimeä tarkoittaa aina, että luku epäonnistui, ei koskaan sitä, että tiedosto olisi laillisesti tyhjä

Epäonnistunut luettelointi on itsessään säilyttämisen arvoinen signaali. .xlsx-tiedosto, jolle kutsu epäonnistuu, on jokin harvoista asioista: katkennut, ei todellisuudessa lainkaan OOXML-paketti (muista järjestelmistä väärin nimettyjä CSV-vientejä näkyy täällä jatkuvasti) tai salattu säilö. Niiden erottaminen toisistaan on seuraavan tarkistuksen tehtävä. Hylätyn tiedoston ensimmäisten tavujen kirjaaminen epäonnistumisen yhteyteen muuttaa tukiketjun yleensä yhdeksi viestiksi

Salattujen säilöjen tunnistaminen ennen reititystä

Salattu .xlsx ei ole zip. Se on OLE-yhdistelmätiedosto, joka käärii EncryptionInfo- ja EncryptedPackage-tietovirrat, joten GetSheetNames ei näe sen sisään ja palauttaa epäonnistumisen kuten minkä tahansa lukukelvottoman tiedoston kohdalla. CanReadEncrypted testaa tämän säilömuodon, jolloin vastaanotto voi reitittää salatun tiedoston tarkoituksellisesti sen sijaan, että se nielisi yleisen lukuvirheen jostain syvältä työntekijäprosessista:

Delphi-vastaanottotriagavirta HotXLS CanReadEncrypted - ja GetSheetNames-kutsuilla, jotka reitittävät lähetykset tarvitsee-salasanan-, lukukelvoton- tai normaali-luokkiin
CanReadEncrypted ajetaan ensin, koska salattu OOXML-tiedosto on OLE-säiliö, jonka sisään luettelointikutsut eivät näe
type
  TIntakeRoute = (irNormal, irNeedsPassword, irUnreadable);

function ClassifyUpload(const FileName: string; Names: TStrings): TIntakeRoute;
var
  Book: TXLSXWorkbook;
begin
  Book := TXLSXWorkbook.Create;
  try
    // Salattu OOXML on OLE-säiliö, ei zip: tarkista ensin,
    // koska luettelointikutsut eivät voi katsoa sen sisään.
    if Book.CanReadEncrypted(FileName) then
      Exit(irNeedsPassword);
    if SameText(ExtractFileExt(FileName), '.ods') then
    begin
      if Book.GetODSSheetNames(FileName, Names) <= 0 then
        Exit(irUnreadable);
    end
    else if Book.GetSheetNames(FileName, Names) <= 0 then
      Exit(irUnreadable);
    Result := irNormal;
  finally
    Book.Free;
  end;
end;

Salaus on kohta, jossa HotXLS on tarkoituksella epäsymmetrinen, joten reitityksen on kunnioitettava sitä. Vanhaa .xls-salausta (RC4, RC4 CryptoAPI, XOR) voidaan lukea: TXLSWorkbook.Open(FileName, Password) purkaa salauksen tallennetulla salasanalla, ja nämä tiedostot voivat pysyä automatisoidulla polulla. Salatut OOXML-paketit toimivat toisin päin. HotXLS voi kirjoittaa sellaisen SaveAsEncrypted-metodilla, mutta se ei voi lukea sitä takaisin. OpenEncrypted nostaa EXlsxEncryptionNotImplemented-poikkeuksen, kun sille annetaan salattu paketti, minkä vuoksi rehellinen vastaanottosuunnittelu lähettää salatun .xlsx-tiedoston henkilölle, jolla on Excel, ja pitää salasanalla varustetun .xls-tiedoston koodissa

Erätyössä tämä luokittelija ansaitsee paikkansa suorittamalla sen koko saapuvalle hakemistolle ennen kuin yksikään työntekijä aloittaa varsinaisen käsittelyn, sillä jokainen tarkistus maksaa suunnilleen yhden tiedostoavauksen ja muutaman kilotavun lukuja. Sen tuominen alkuvaiheeseen muuttaa toimintojen kannalta merkityksellisen epäonnistumistilan. Sen sijaan, että klo 3 yöllä ajettava työ kuolisi tiedostoon 412/600, saat 412 tiedostoa jonoon ja 5 hylättyä vastaanotossa, jokaiselle syy liitettynä. Samat kirjastokutsut, paljon parempi operatiivinen tarina

Kysymykset, joihin luettelointikutsu ei voi vastata

Nimet ja järjestys ovat kaikki, mitä saat. Luettelointikutsut eivät kerro mitään näkyvyydestä, joten piilotetut ja erittäin piilotetut taulukot tulevat luetteloon kuin mitkä tahansa muutkin. Ne eivät ilmoita käytetyn alueen mittoja, solumääriä tai asiakirjan ominaisuuksia. docProps/core.xml-osa on myös pieni, mutta ominaisuuksille ei ole tänään vain-ominaisuudet-tarkistusta, joten tekijän ja otsikon metatiedot vaativat edelleen täyden Open-kutsun. Siisti tapa elää tämän kanssa on antaa halpojen faktojen reitittää jokainen tiedosto ja varata kalliit vain reitityksen läpäiseville tiedostoille. Syvälukuun eteneville tiedostoille suuren .xls-tiedoston vain luku -skannaus toimii huomattavasti nopeammin, kun käytössä on _DisableGraphics := True, joka ohittaa OfficeArt-jäsennyksen. Älä vain koskaan tallenna tästä esiintymästä: sen ohittama piirrostaso puuttuu mallista, ja tallennus pudottaisi sen tiedostosta

Triagen läpäisseet tiedostot etenevät yleensä syvempään analyysiin. työkirjan auditointi- ja muunnostyöpöytä käsittelee taulukkokohtaisia laskureita, jotka kannattaa kerätä, kun täysi avaus on perusteltu, ja suurten työkirjojen suorituskykyopas käsittelee tämän täyden avauksen pitämistä nopeana

HotXLS on Delphin ja C++Builderin natiivi Object Pascal -laskentataulukkokirjasto; täydellinen API-rajapinta, mukaan lukien tässä esitetyt tarkistuskutsut, on dokumentoitu HotXLS Delphi Component -tuotesivulla