Technical Article

Извличане на обобщена информация за документи от Excel файлове в Delphi

Когато обработвате големи партиди Excel електронни таблици в автоматизиран конвейер, рядко искате да заредите целия документ в паметта, само за да разберете какъв е той. Често метаданните, вградени във файла (автор, заглавие, дата на създаване и персонализирани свойства), са достатъчни за маршрутизиране, индексиране или отхвърляне на документа. В света на Microsoft Office тези метаданни са известни като Обобщена информация за документа (Document Summary Information).

Екстрахирането на тази информация нативно в Delphi, без да се разчита на OLE автоматизация (което изисква Excel да бъде инсталиран на хост машината), налага директно анализиране на основната файлова структура. В тази статия ще разгледаме как работят обобщенията на документи в Excel файловете и как да ги извличаме ефективно чрез синтактичен анализ на необработени потоци.

Разбиране на потоците от метаданни в Excel

Исторически по-старите Excel файлове (.xls) се съхраняват във формати OLE Compound Document, действайки ефективно като мини файлови системи, съдържащи потоци и хранилища. Метаданните се намират в два специфични потока:

  • SummaryInformation: Съдържа стандартни свойства като Title, Subject, Author, Keywords и Revision Number.
  • DocumentSummaryInformation: Съдържа разширени свойства като Company, Manager и персонализирани дефинирани от потребителя свойства.

Модерните Excel файлове (.xlsx) използват формата Office Open XML (OOXML), който е компресирана XML структура. Тук метаданните се намират в docProps/core.xml, docProps/app.xml и docProps/custom.xml. Здравият компонент за синтактичен анализ в Delphi трябва безпроблемно да се справя и с двете вътрешни структури, докато излага унифициран API към разработчика.

Анализиране на OLE Compound документи в Delphi

За да прочетете SummaryInformation от наследен `.xls` файл без инструменти на трети страни, трябва да анализирате OLE Structured Storage. Microsoft излага това чрез COM интерфейса IPropertySetStorage. Ето необработена имплементация в Delphi, която избягва стартирането на Excel:

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;

Програмно извличане с HotXLS

Докато Windows COM API работи за `.xls` файлове, той не работи за модерни `.xlsx` файлове (които са ZIP архиви). Освен това, използването на COM API междуплатформено (например на Linux или macOS чрез FireMonkey) е невъзможно. Последните актуализации на компонента HotXLS въведоха специализирани единици (напр. lxXlsSummary) за изолиране и оптимизиране на четенето на тези обобщаващи потоци в двата формата напълно нативно в Delphi код.

Пример за междуплатформено приложение

Използвайки интерфейсите XlsReadDocumentSummaryInformation и XlsReadSummaryInformation, можете бързо да вземете низовете с метаданни както от `.xls`, така и от `.xlsx`, без да се притеснявате за архитектурата на основната файлова система.

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;

Защо е важно специализираното извличане на обобщения

Основната полза от този подход е производителността и безопасността на паметта. Като се избягва създаването на екземпляр на пълния DOM (Модел на обектния документ) на работната книга и се анализират само docProps/core.xml или OLE потоците със свойства, отпечатъкът на вашето приложение остава невероятно малък. Ако индексирате 10 000 Excel файла в мрежов дял, опитът за пълно анализиране на всеки един от тях ще претовари паметта ви и ще отнеме часове. Специализираното извличане на обобщения изпълнява същата задача за секунди.

Освен това, нативното четене на потоците гарантира, че вашето приложение може да работи като фонова услуга или на сървър на Linux без монитор, без изобщо да извиква Excel.exe, което е критично изискване за съвременните мащабируеми архитектури.

Забележка: Изчерпателни инструменти за синтактичен анализ на Excel и извличане на метаданни са налични в HotXLS VCL Component.