בעת עיבוד קבוצות גדולות של גיליונות אלקטרוניים של Excel בצינור נתונים אוטומטי (automated pipeline), לעיתים רחוקות תרצו לטעון את המסמך כולו לזיכרון רק כדי להבין מהו. לעיתים קרובות, המטא-נתונים המוטמעים בתוך הקובץ - מחבר, כותרת, תאריך יצירה ומאפיינים מותאמים אישית - מספיקים כדי לנתב, למפתח או לדחות את המסמך. בעולם של Microsoft Office, מטא-נתונים אלה ידועים בשם Document Summary Information (מידע סיכום מסמך).
חילוץ מידע זה באופן מקורי (natively) ב-Delphi ללא הסתמכות על אוטומציה של OLE (הדורשת התקנה של Excel במכונה המארחת) דורש ניתוח ישיר של מבנה הקובץ הבסיסי. במאמר זה, נבחן כיצד סיכומי מסמכים פועלים בקבצי Excel וכיצד לחלץ אותם ביעילות באמצעות ניתוח זרמים גולמי (raw stream parsing).
הבנת זרמי מטא-נתונים של Excel
מבחינה היסטורית, קבצי Excel ישנים יותר (.xls) מאוחסנים בפורמטים של OLE Compound Document, ופועלים למעשה כמערכות קבצים קטנות המכילות זרמים ואחסונים. המטא-נתונים שוכנים בשני זרמים ספציפיים:
SummaryInformation: מכיל מאפיינים סטנדרטיים כמו כותרת, נושא, מחבר, מילות מפתח ומספר מהדורה.DocumentSummaryInformation: מכיל מאפיינים מורחבים כגון חברה, מנהל ומאפיינים מותאמים אישית המוגדרים על ידי המשתמש.
קבצי Excel מודרניים (.xlsx) משתמשים בפורמט Office Open XML (OOXML), שהוא מבנה XML דחוס ב-ZIP. המטא-נתונים כאן ממוקמים ב-docProps/core.xml, docProps/app.xml, ו-docProps/custom.xml. רכיב ניתוח רובסטי ב-Delphi חייב לטפל בצורה חלקה בשני המבנים הפנימיים תוך חשיפת ממשק API אחיד למפתח.
ניתוח מסמכי OLE Compound ב-Delphi
כדי לקרוא את ה-SummaryInformation מקובץ .xls מדור קודם ללא כלי צד שלישי, עליכם לנתח את ה-OLE Structured Storage. מיקרוסופט חושפת זאת באמצעות ממשק ה-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
בעוד שממשק ה-COM API של Windows עובד עבור קבצי .xls, הוא אינו עובד עבור קבצי .xlsx מודרניים (שהם ארכיוני ZIP). יתרה מכך, שימוש בממשק COM API חוצה-פלטפורמות (למשל, ב-Linux או macOS דרך FireMonkey) הוא בלתי אפשרי. עדכונים אחרונים לרכיב HotXLS הציגו יחידות ייעודיות (למשל, lxXlsSummary) כדי לבודד ולמטב את הקריאה של זרמי סיכום אלה בשני הפורמטים, באופן מקורי לחלוטין (natively) בקוד 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, חותמת הזיכרון של היישום שלכם נשארת קטנה להפליא. אם אתם מפתחים (indexing) 10,000 קבצי Excel ברשת משותפת, ניסיון לנתח באופן מלא כל אחד מהם יכביד על הזיכרון וייקח שעות. חילוץ סיכום ייעודי מסיים את אותה משימה בשניות.
יתרה מכך, קריאת הזרמים באופן מקורי מבטיחה שהיישום שלכם יכול לפעול כשירות רקע (background service) או בשרת Linux ללא ממשק גרפי (headless) מבלי להפעיל אי פעם את Excel.exe - דרישה קריטית עבור ארכיטקטורות מודרניות הניתנות להרחבה.
הערה: כלי ניתוח Excel וחילוץ מטא-נתונים מקיפים זמינים ב-HotXLS VCL Component.