Technical Article

استخراج اطلاعات خلاصه سند از فایل‌های اکسل در دلفی

هنگام پردازش دسته‌های بزرگ صفحات گسترده اکسل در یک خط لوله خودکار، به ندرت می‌خواهید کل سند را در حافظه بارگذاری کنید فقط برای اینکه بفهمید چیست. اغلب، متادیتای تعبیه‌شده درون فایل (مانند نویسنده، عنوان، تاریخ ایجاد و ویژگی‌های سفارشی) برای مسیریابی، نمایه‌سازی یا رد کردن سند کافی است. در دنیای مایکروسافت آفیس، این متادیتا با عنوان اطلاعات خلاصه سند (Document Summary Information) شناخته می‌شود.

استخراج این اطلاعات به صورت بومی در دلفی بدون اتکا به اتوماسیون OLE (که نیازمند نصب اکسل بر روی ماشین میزبان است)، مستلزم تجزیه مستقیم ساختار فایل پایه است. در این مقاله، نحوه عملکرد خلاصه‌های سند در فایل‌های اکسل و نحوه استخراج کارآمد آن‌ها با استفاده از تجزیه مستقیم جریان‌ها را بررسی خواهیم کرد.

درک جریان‌های متادیتای اکسل

از نظر تاریخی، فایل‌های قدیمی‌تر اکسل (xls.) در قالب‌های OLE Compound Document ذخیره می‌شدند که عملاً به عنوان سیستم‌فایل‌های کوچکی شامل جریان‌ها و فضای ذخیره‌سازی عمل می‌کردند. متادیتا در دو جریان خاص قرار دارد:

  • SummaryInformation: شامل ویژگی‌های استانداردی مانند عنوان (Title)، موضوع (Subject)، نویسنده (Author)، کلمات کلیدی (Keywords) و شماره بازبینی (Revision Number) است.
  • DocumentSummaryInformation: شامل ویژگی‌های توسعه‌یافته‌ای نظیر شرکت (Company)، مدیر (Manager) و ویژگی‌های سفارشی تعریف‌شده توسط کاربر است.

فایل‌های مدرن اکسل (xlsx.) از فرمت Office Open XML (OOXML) استفاده می‌کنند که یک ساختار XML فشرده (زیپ شده) است. در اینجا، متادیتا در فایل‌های docProps/core.xml، docProps/app.xml و docProps/custom.xml قرار دارد. یک کامپوننت قدرتمند تجزیه در دلفی باید به طور یکپارچه هر دو ساختار داخلی را مدیریت کرده و در عین حال یک API واحد را در اختیار توسعه‌دهنده قرار دهد.

تجزیه اسناد OLE Compound در دلفی

برای خواندن SummaryInformation از یک فایل قدیمی xls. بدون ابزارهای شخص ثالث، باید ساختار OLE Structured Storage را تجزیه کنید. مایکروسافت این امکان را از طریق رابط COM به نام IPropertySetStorage فراهم می‌کند. در اینجا یک پیاده‌سازی خالص دلفی آورده شده است که از اجرای اکسل جلوگیری می‌کند:

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 در بسترهای چندگانه (مثلاً لینوکس یا macOS از طریق FireMonkey) غیرممکن است. به‌روزرسانی‌های اخیر کامپوننت HotXLS واحدهای اختصاصی (مانند lxXlsSummary) را برای جداسازی و بهینه‌سازی خواندن این جریان‌های خلاصه در هر دو فرمت، کاملاً به صورت بومی در کدهای دلفی، معرفی کرده است.

یک مثال چند پلتفرمی

با استفاده از رابط‌های 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، ردپای حافظه برنامه شما به طرز شگفت‌انگیزی کوچک باقی می‌ماند. اگر در حال نمایه‌سازی ده‌هزار فایل اکسل در یک شبکه اشتراکی هستید، تلاش برای تجزیه کامل هر یک از آن‌ها حافظه شما را درگیر کرده و ساعت‌ها زمان می‌برد. استخراج اختصاصی خلاصه، همین کار را در چند ثانیه به پایان می‌رساند.

علاوه بر این، خواندن بومی جریان‌ها تضمین می‌کند که برنامه شما می‌تواند به عنوان یک سرویس پس‌زمینه یا روی یک سرور لینوکس بدون رابط کاربری گرافیکی اجرا شود، بدون اینکه نیازی به فراخوانی Excel.exe داشته باشد؛ که این یک نیاز حیاتی برای معماری‌های مدرن و مقیاس‌پذیر است.

توجه: ابزارهای جامع تجزیه اکسل و استخراج متادیتا در کامپوننت HotXLS VCL در دسترس هستند.