Technical Article

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

Apdorojant dideles „Excel“ skaičiuoklių partijas automatizuotame procese, retai kada norima įkelti visą dokumentą į atmintį vien tam, kad sužinotumėte, kas tai yra. Dažnai faile įterptų metaduomenų (autoriaus, pavadinimo, sukūrimo datos ir pasirinktinių savybių) pakanka norint nukreipti, indeksuoti ar atmesti dokumentą. „Microsoft Office“ pasaulyje šie metaduomenys žinomi kaip dokumentų suvestinės informacija (angl. Document Summary Information).

Norint šią informaciją natūraliai išskirti „Delphi“ aplinkoje, nepasikliaujant OLE automatizavimu (kuriam reikalaujama, kad prieglobos kompiuteryje būtų įdiegtas „Excel“), reikia tiesiogiai analizuoti pagrindinę failo struktūrą. Šiame straipsnyje apžvelgsime, kaip veikia dokumentų suvestinės „Excel“ failuose ir kaip jas efektyviai išskirti naudojant neapdorotų srautų (angl. raw stream) analizę.

„Excel“ metaduomenų srautų supratimas

Istoriškai senesni „Excel“ failai (.xls) saugomi „OLE Compound Document“ formatu, kuris iš esmės veikia kaip mini failų sistema, talpinanti srautus ir saugyklas. Metaduomenys saugomi dviejuose specifiniuose srautuose:

  • SummaryInformation: talpina standartines savybes, tokias kaip pavadinimas, tema, autorius, raktažodžiai ir redakcijos numeris.
  • DocumentSummaryInformation: talpina išplėstines savybes, tokias 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. Metaduomenys čia yra saugomi failuose docProps/core.xml, docProps/app.xml ir docProps/custom.xml. Tvirtas „Delphi“ analizavimo komponentas turi sklandžiai apdoroti abi vidines struktūras ir kūrėjui pateikti vieningą API.

OLE sudėtinių dokumentų analizė „Delphi“ aplinkoje

Norint nuskaityti „SummaryInformation“ iš senojo formato .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 neapdorotas „Delphi“ diegimas, padedantis 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 seniesiems .xls failams, ji neveikia šiuolaikiniams .xlsx failams (kurie yra ZIP archyvai). Be to, naudoti COM API įvairiose platformose (pvz., „Linux“ ar „macOS“ per „FireMonkey“) yra neįmanoma. Naujausiuose „HotXLS“ komponento atnaujinimuose įdiegti specialūs moduliai (pvz., lxXlsSummary), skirti visiškai natūraliai (naudojant „Delphi“ kodą) izoliuoti ir optimizuoti šių suvestinės srautų skaitymą abiem formatams.

Kelių platformų pavyzdys

Naudodami XlsReadDocumentSummaryInformation ir XlsReadSummaryInformation sąsajas, galite greitai gauti metaduomenų eilutes tiek iš .xls, tiek iš .xlsx nesirūpindami pagrindine failų sistemos architektūra.

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 svarbus specializuotas suvestinės išskyrimas

Pagrindinis šio metodo privalumas yra našumas ir atminties saugumas. Išvengiant viso darbo knygos DOM (angl. Document Object Model) egzempliorių kūrimo ir analizuojant tik docProps/core.xml arba OLE savybių srautus, jūsų programos resursų naudojimas išlieka neįtikėtinai mažas. Jei indeksuojate 10 000 „Excel“ failų tinklo saugykloje, bandymas visiškai išanalizuoti kiekvieną iš jų išeikvos jūsų atmintį ir užtruks valandas. Specializuotas 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ė paslauga arba be galvutės (angl. headless) „Linux“ serveryje niekada neiškviesdama „Excel.exe“, o tai yra esminis reikalavimas šiuolaikinėms keičiamo dydžio architektūroms.

Pastaba: išsamūs „Excel“ analizės ir metaduomenų išskyrimo įrankiai yra prieinami „HotXLS VCL“ komponente.