Prilikom obrade velikih serija Excel proračunskih tablica u automatiziranom cjevovodu, rijetko želite učitati cijeli dokument u memoriju samo kako biste shvatili o čemu se radi. Često su metapodaci ugrađeni unutar datoteke, kao što su autor, naslov, datum stvaranja i prilagođena svojstva, dovoljni za usmjeravanje, indeksiranje ili odbacivanje dokumenta. U svijetu Microsoft Officea, ti metapodaci poznati su kao informacije sažetka dokumenta (Document Summary Information).
Za izvorno izdvajanje ovih informacija u Delphiju bez oslanjanja na OLE automatizaciju (koja zahtijeva instaliran Excel na glavnom stroju) potrebno je izravno parsirati temeljnu strukturu datoteke. U ovom ćemo članku pogledati kako sažeci dokumenata funkcioniraju u Excel datotekama i kako ih učinkovito izdvojiti pomoću parsiranja sirovih tokova.
Razumijevanje tokova Excel metapodataka
Povijesno gledano, starije Excel datoteke (.xls) pohranjene su u OLE Compound Document formatima, koji efektivno djeluju kao mini datotečni sustavi koji sadrže tokove i pohrane. Metapodaci su smješteni u dva specifična toka:
SummaryInformation: Sadrži standardna svojstva kao što su naslov, predmet, autor, ključne riječi i broj revizije.DocumentSummaryInformation: Sadrži proširena svojstva kao što su tvrtka, menadžer i prilagođena korisnička svojstva.
Moderne Excel datoteke (.xlsx) koriste format Office Open XML (OOXML), koji je zipovana XML struktura. Metapodaci se ovdje nalaze u docProps/core.xml, docProps/app.xml i docProps/custom.xml. Robusna Delphi komponenta za parsiranje mora besprijekorno rukovati objema unutarnjim strukturama, a istovremeno izložiti jedinstveni API programeru.
Parsiranje OLE Compound dokumenata u Delphiju
Da biste pročitali SummaryInformation iz naslijeđene .xls datoteke bez alata trećih strana, morate parsirati OLE Structured Storage. Microsoft to izlaže kroz COM sučelje IPropertySetStorage. Ovdje je sirova Delphi implementacija koja izbjegava pokretanje Excela:
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;
Programsko izdvajanje pomoću HotXLS-a
Iako Windows COM API radi za .xls datoteke, on ne radi za moderne .xlsx datoteke (koje su ZIP arhive). Nadalje, korištenje COM API-ja na više platformi (npr. na Linuxu ili macOS-u putem FireMonkeyja) je nemoguće. Nedavna ažuriranja komponente HotXLS uvela su namjenske jedinice (npr. lxXlsSummary) za izoliranje i optimizaciju čitanja ovih tokova sažetaka u oba formata potpuno izvorno u Delphi kodu.
Primjer za više platformi
Koristeći sučelja XlsReadDocumentSummaryInformation i XlsReadSummaryInformation, možete brzo preuzeti nizove metapodataka i iz .xls i iz .xlsx bez brige o temeljnoj arhitekturi datotečnog sustava.
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;
Zašto je namjensko izdvajanje sažetka važno
Primarna prednost ovog pristupa je izvedba i memorijska sigurnost. Izbjegavanjem instanciranja cjelokupnog DOM-a radne knjige (Document Object Model) i parsiranjem samo docProps/core.xml ili OLE tokova svojstava, otisak vaše aplikacije ostaje nevjerojatno mali. Ako indeksirate 10.000 Excel datoteka preko mrežnog dijeljenja, pokušaj potpunog parsiranja svake od njih iscrpit će vašu memoriju i trajati satima. Namjensko izdvajanje sažetka obavlja isti zadatak u sekundi.
Nadalje, izvorno čitanje tokova osigurava da se vaša aplikacija može izvoditi kao pozadinska usluga ili na Linux poslužitelju bez grafičkog sučelja (headless) bez ikakvog pozivanja Excel.exe procesa, što je ključan zahtjev za moderne skalabilne arhitekture.
Napomena: Sveobuhvatni alati za parsiranje Excela i izdvajanje metapodataka dostupni su u HotXLS VCL Component.