Techninis straipsnis

Dokumentų suvestinės informacijos išskyrimas iš „Excel“ failų Delphi aplinkoje

Apdorojant dideles „Excel“ skaičiuoklių partijas automatizuotame konvejeryje (angl. automated pipeline), retai norima įkelti visą dokumentą į atmintį vien tam, kad išsiaiškintumėte, kas tai yra. Dažnai faile įterptų metaduomenų (autorius, pavadinimas, sukūrimo data ir pasirinktinės savybės) pakanka dokumentui nukreipti, indeksuoti arba atmesti. „Microsoft Office“ pasaulyje šie metaduomenys žinomi kaip Dokumentų suvestinės informacija (angl. Document Summary Information)

Norint išgauti šią informaciją natūraliai Delphi aplinkoje, nepasikliaujant OLE automatizavimu (kuris reikalauja, kad pagrindiniame kompiuteryje būtų įdiegtas „Excel“), reikia tiesiogiai analizuoti (angl. parse) pagrindinę failo struktūrą. Tolesniuose skyriuose aptariama, kaip dokumentų suvestinės saugomos „Excel“ failuose ir kaip jas efektyviai išskirti naudojant tiesioginį srautų analizavimą

„Excel“ metaduomenų srautų supratimas

Istoriškai senesni „Excel“ failai (.xls) saugomi „OLE Compound Document“ formatu, veiksmingai veikiančiu kaip mini failų sistemos, turinčios srautus ir saugyklas. Metaduomenys saugomi dviejuose specifiniuose srautuose:

  • SummaryInformation: Sudėtyje yra standartinės savybės, tokios kaip Pavadinimas, Tema, Autorius, Raktiniai žodžiai ir Versijos numeris
  • DocumentSummaryInformation: Sudėtyje yra išplėstinės savybės, tokios kaip Įmonė, Vadovas ir pasirinktinės vartotojo apibrėžtos savybės

Šiuolaikiniai „Excel“ failai (.xlsx) naudoja „Office Open XML“ (OOXML) formatą, kuris yra suglaudinta XML struktūra. Čia metaduomenys yra docProps/core.xml, docProps/app.xml ir docProps/custom.xml failuose. Patikimas Delphi analizavimo komponentas turi apdoroti abi vidines struktūras be papildomo skambinančiojo darbo, tuo pačiu kūrėjui atskleisdamas vieningą API

OLE sudėtinių dokumentų analizavimas Delphi aplinkoje

Norėdami perskaityti SummaryInformation iš senojo .xls failo be trečiųjų šalių įrankių, turite išanalizuoti OLE struktūrinę saugyklą. „Microsoft“ tai atskleidžia per COM sąsają IPropertySetStorage. Štai tiesioginė Delphi realizacija, leidžianti išvengti „Excel“ paleidimo:

uses
  System.SysUtils, System.Win.ComObj, Winapi.ActiveX, Winapi.Windows;

procedure ExtractXlsSummaryInfo(const FileName: string);
var
  Stg: IStorage;
  PropSetStg: IPropertySetStorage;
  PropStg: IPropertyStorage;
  PropSpec: TPropSpec;
  PropVariant: TPropVariant;
  Hr: HRESULT;
begin
  // Open the OLE Compound Document
  Hr := StgOpenStorage(PWideChar(WideString(FileName)), nil,
    STGM_READ or STGM_SHARE_DENY_WRITE, nil, 0, Stg);
    
  if Failed(Hr) then
    raise Exception.Create('Failed to open OLE storage. File may not be a valid .xls document.');

  // Query for the property set storage interface
  if Stg.QueryInterface(IPropertySetStorage, PropSetStg) = S_OK then
  begin
    // Open the SummaryInformation stream (FMTID_SummaryInformation)
    Hr := PropSetStg.Open(FMTID_SummaryInformation, STGM_READ or STGM_SHARE_EXCLUSIVE, PropStg);
    if Succeeded(Hr) then
    begin
      // Read the Author property (PIDSI_AUTHOR = 4)
      PropSpec.ulKind := PRSPEC_PROPID;
      PropSpec.propid := PIDSI_AUTHOR;
      
      if PropStg.ReadMultiple(1, @PropSpec, @PropVariant) = S_OK then
      begin
        if PropVariant.vt = VT_LPSTR then
          Writeln('Author: ', string(AnsiString(PropVariant.pszVal)));
        PropVariantClear(PropVariant);
      end;
    end;
  end;
end;

Programinis išskyrimas su HotXLS

Nors „Windows“ COM API veikia .xls failams, jis neveikia šiuolaikiniams .xlsx failams (kurie yra ZIP archyvai). Be to, naudoti COM API įvairiose platformose (pvz., Linux ar macOS per FireMonkey) yra neįmanoma. Naujausi HotXLS komponento atnaujinimai įvedė dedikuotus modulius (pvz., lxXlsSummary), siekiant izoliuoti ir optimizuoti šių suvestinės srautų skaitymą abiejuose formatuose visiškai natūraliai Delphi kode

Daugiaplatformis pavyzdys

Naudodami XlsReadDocumentSummaryInformation ir XlsReadSummaryInformation sąsajas, galite greitai paimti metaduomenų eilutes iš .xls ir .xlsx, nesijaudindami dėl pagrindinės failų sistemos architektūros

uses
  lxXlsSummary;

var
  Summary: TXlsSummaryInfo;
  ExtendedInfo: TXlsDocumentSummaryInfo;
begin
  // Extract standard summary from an OOXML format seamlessly
  Summary := XlsReadSummaryInformation('C:\Data\FinancialReport.xlsx');
  try
    Writeln('Title: ', Summary.Title);
    Writeln('Author: ', Summary.Author);
    Writeln('Creation Date: ', DateTimeToStr(Summary.CreateTime));
  finally
    Summary.Free;
  end;

  // Extract extended document summary
  ExtendedInfo := XlsReadDocumentSummaryInformation('C:\Data\FinancialReport.xlsx');
  try
    Writeln('Company: ', ExtendedInfo.Company);
    Writeln('Manager: ', ExtendedInfo.Manager);
  finally
    ExtendedInfo.Free;
  end;
end;

Kodėl dedikuotas suvestinės išskyrimas yra svarbus

Pagrindinis šio požiūrio privalumas yra našumas ir atminties saugumas. Išvengiant pilno darbo knygos DOM (Document Object Model) egzemplioriaus sukūrimo ir analizuojant tik docProps/core.xml arba OLE savybių srautus, jūsų programos pėdsakas išlieka neįtikėtinai mažas. Jei indeksuojate 10 000 „Excel“ failų tinklo diske, bandymas pilnai išanalizuoti kiekvieną iš jų išeikvos jūsų atmintį ir užtruks valandas. Dedikuotas suvestinės išskyrimas tą pačią užduotį atlieka per kelias sekundes

Be to, natūralus srautų skaitymas užtikrina, kad jūsų programa gali veikti kaip foninė tarnyba arba „Linux“ serveryje be vaizduoklio (angl. headless), niekada neiškviečiant Excel.exe – tai kritinis reikalavimas šiuolaikinėms keičiamo mastelio (angl. scalable) architektūroms

Pastaba: Pilni „Excel“ analizavimo ir metaduomenų išskyrimo įrankiai yra prieinami HotXLS VCL komponente