مقاله فنی

تنظیم ویژگی‌های سند اکسل و متادیتای کتاب کار در دلفی با HotXLS

یک صفحه‌گسترده دو لایه هویتی را با خود حمل می‌کند. شبکه سلول‌ها وجود دارد، و متادیتای سند که در کنار آن حرکت می‌کند: عنوان، نویسنده، شرکت، کلمات کلیدی، و مهرهای زمانی. اکسل هرگز این لایه دوم را در جدول نشان نمی‌دهد، اما این همان لایه‌ای است که جستجوی ویندوز (Windows Search) آن را اندیس‌گذاری می‌کند، همان لایه‌ای که شیرپوینت (SharePoint) برای عنوان سند می‌خواند، و همان لایه‌ای که سیستم مدیریت اسناد بر اساس آن کار بایگانی را انجام می‌دهد. وقتی یک کتاب کار تولید شده، نویسنده و عنوان خود را از قالبی که از روی آن ساخته شده است به ارث می‌برد، هر سیستم پایین‌دستی طراح قالب را به عنوان نویسنده چهار هزار صورتحساب مشتری ثبت می‌کند. متادیتا در هیچ‌کجا صحیح نیست اما در همه‌جا مورد مشورت قرار می‌گیرد

HotXLS این لایه را به عنوان ویژگی‌های معمولی در سطح کتاب کار در هر دو موتور خود ارائه می‌دهد: رابط BIFF برای .xls و رابط OOXML برای .xlsx. شما یک فیلد را پس از باز کردن فایل می‌خوانید و فیلدی را قبل از ذخیره کردن می‌نویسد. کتابخانه تصمیم می‌گیرد که مقدار در کدام کانتینر فیزیکی قرار گیرد. آنچه ارزش درک دارد قبل از اینکه یک مولد بنویسید این است که هر فرمت در واقع از کدام فیلدها پشتیبانی می‌کند، آن فیلدها در کجا به طور فیزیکی قرار دارند، و قانون دروازه‌ای که تعیین می‌کند آیا یک فایل .xlsx اصلاً متادیتایی را ثبت می‌کند یا خیر

دو فرمت، دو مدل ذخیره‌سازی

دلیل اینکه یک کتابخانه صفحه‌گسترده به دو پیاده‌سازی متادیتا نیاز دارد، و دلیل اینکه ابزارهای نیمه‌کاره یک فرمت را به درستی ثبت می‌کنند و فرمت دیگر را فراموش می‌نمایند، این است که .xls و .xlsx ویژگی‌های خود را در مکان‌های غیرمرتبط نگه می‌دارند. یک کتاب کار BIFF آن‌ها را در جریان‌های فایل مرکب OLE می‌نویسد، عمدتاً مجموعه ویژگی‌های SummaryInformation که قدمت آن بیش از خود اکسل است، در کنار رکورد درون‌جریانی WRITEACCESS که نام آخرین کسی که فایل را ذخیره کرده است را مشخص می‌کند. یک کتاب کار OOXML آن‌ها را به عنوان بخش‌های XML در داخل بسته فشرده (zip) نگه می‌دارد که بر اساس کاربرد تقسیم شده‌اند: docProps/core.xml فیلدهای هسته دوبلین (Dublin Core) مانند عنوان، سازنده، موضوع، کلمات کلیدی و تاریخ‌ها را نگه می‌دارد و docProps/app.xml فیلدهای سطح برنامه مانند شرکت و برنامه تولیدکننده را طبق بخش 1 استاندارد ECMA-376 نگه می‌دارد

HotXLS هر دوی این مدل‌های ذخیره‌سازی را به ویژگی‌های مستقیم شیء کتاب کار مسطح می‌کند. شما هرگز یک جریان مجموعه ویژگی را باز نمی‌کنید یا یک بخش XML را به طور دستی ویرایش نمی‌نمایید. شما رشته‌ها و تاریخ‌ها را به کتاب کار اختصاص می‌دهید و کانتینر صحیح برای هر فرمتی که ذخیره می‌کنید ظاهر می‌شود

ثبت اطلاعات کتاب‌های کار تولید شده از روی سوابق کاری

در سمت XLSX، کلاس TXLSXWorkbook ویژگی‌های Title، Subject، Author، Keywords، Description، Category، LastModifiedBy، Company، Application، و AppVersion را به عنوان رشته، به علاوه Created و Modified را به عنوان مقادیر TDateTime (جایی که صفر به معنای تنظیم‌نشده است) ارائه می‌دهد. قانونی که حفره ارث‌بری را می‌بندد این است: در هر بار اجرا تمام فیلدها را مقداردهی کنید و مقادیر را از سوابق واقعی کاری بگیرید به جای اینکه به هر آنچه که قالب به همراه دارد اعتماد کنید

var
  Book: TXLSXWorkbook;
begin
  Book := TXLSXWorkbook.Create;
  try
    if Book.Open('statement-template.xlsx') <> 1 then
      raise Exception.Create('Template not available');

    // رونویسی تمام فیلدها: هر چیزی که دست‌نخورده باقی بماند از
    // کسی که قالب را طراحی کرده است به ارث می‌رسد.
    Book.Title := 'Account Statement 2026-06 / ACME Corp';
    Book.Subject := 'Monthly account statement';
    Book.Author := 'Billing Service 4.2';
    Book.LastModifiedBy := 'Billing Service 4.2';
    Book.Company := 'Northwind Financial';
    Book.Category := 'Customer Delivery';
    Book.Keywords := 'statement;billing;2026-06;acct-10024';
    Book.Description := 'Generated document - manual edits are not retained';
    Book.Created := Now;
    Book.Modified := Now;

    Book.SaveAs('statement-10024.xlsx');
  finally
    Book.Free;
  end;
end;

فیلد Keywords بیش از آنچه معمولاً به آن توجه می‌شود ارزش فکر کردن دارد. زیرساخت جستجو آن را به صورت کلمه به کلمه اندیس‌گذاری می‌کند، جستجوی ویندوز، شیرپوینت و اکثر محصولات DMS به طور مشابه؛ بنابراین یک قرارداد جداشده با نقطه ویرگول حاوی شماره حساب و دوره زمانی، هر کتاب کار تحویل داده شده را بدون نیاز به رفت و برگشت به پایگاه داده به یک رکورد قابل جستجو تبدیل می‌کند. اما همین دسترسی گسترده یک تله است. ویژگی‌ها با هر کپی از فایل منتقل می‌شوند، و از کنترل‌های دسترسی سیستمی که آن‌ها را نوشته است فراتر می‌روند، بنابراین داده‌های شخصی جایی در آنجا ندارند

جفت مهر زمانی (timestamp pair) معناشناسی را به همراه دارد که ارزش دارد در سیاست‌های کاری مشخص شود تا اینکه به عادت واگذار گردد. Created باید لحظه‌ای را نشان دهد که خط لوله شما سند را تولید کرده است و سپس ثابت بماند. Modified فیلدی است که اکسل هر زمان که گیرنده فایل را ذخیره کند به‌روزرسانی می‌نماید، بنابراین اختلاف بین این دو پس از تحویل، شواهد مثبتی است مبنی بر اینکه شخصی کتاب کار را در پایین‌دستی ویرایش کرده است، که این موضوع بسیاری از اختلافات را در مورد اینکه یک صفحه‌گسترده فرستاده شده واقعاً متعلق به چه کسی است حل می‌کند. یک تله در حالت تنظیم‌نشده پنهان است: این مقدار دقیقاً صفر است، نه یک استثنا و نه null، بنابراین کد حسابرسی باید به طور صریح صفر را آزمایش کند. قالب‌بندی یک TDateTime تنظیم‌نشده بدون این محافظ، گزارش‌های شما را با یک تاریخ کاملاً اشتباه دسامبر 1899 پر خواهد کرد

DocPropsTouched: کتاب کاری که بدون docProps ارسال می‌شود

یک پرچم فقط‌خواندنی، یعنی DocPropsTouched، خروجی ویژگی‌های XLSX را کنترل می‌کند. کتاب کاری که در آن هیچ ویژگی‌ای هرگز اختصاص داده نشده است، هیچ بخشی از docProps را تولید نمی‌کند؛ HotXLS از نوشتن یک اسکلت متادیتای خالی خودداری می‌نماید. این رفتار تمیز است و دو پیامد دارد که ارزش طراحی بر اساس آن‌ها را دارد

کد دریافت در سمت مصرف‌کننده نباید فرض کند که فایل core.xml در هر بسته‌ای وجود دارد. ابزاری که آن را به طور سخت‌گیرانه نیاز دارد، فایل‌های حداقلی و کاملاً معتبر را رد خواهد کرد. و اگر الزامات انطباق شما ایجاب می‌کند که هر سند خروجی حداقل هویت مولد را به همراه داشته باشد، این تقاضا به کد تبدیل می‌شود و نه ویژگی فرمت: متغیرهای Application و Author را بدون قید و شرط در مسیر ذخیره اختصاص دهید، زیرا یک کتاب کار دست‌نخورده از نظر مشخصات کاملاً قانونی است در حالی که بی‌صدا سیاست شما را نقض می‌کند

رابط قدیمی XLS و تله کامنت‌ها

رابط BIFF مجموعه فیلدهای قدیمی‌تر و کوچک‌تری را به همراه دارد: Title، Subject، Author، Keywords، Comments، Company، و Manager، به علاوه LastSavedBy که نام مستعار UserName است و رکورد WRITEACCESS را می‌نویسد که اکسل هنگام قفل بودن فایل توسط کاربر دیگر نشان می‌دهد

var
  Legacy: IXLSWorkbook;     // مراجع اینترفیس: بدون نیاز به Free دستی
begin
  Legacy := TXLSWorkbook.Create;
  if Legacy.Open('archive-1999.xls') <= 0 then
    raise Exception.Create('Cannot open archive file');

  Legacy.Title := 'FY1999 ledger (migrated copy)';
  Legacy.Author := 'Archive Migration Batch';
  Legacy.Company := 'Northwind Financial';
  Legacy.Comments := 'Migrated 2026-06-11; source retained in cold storage';
  Legacy.LastSavedBy := 'migration-svc';   // BIFF WRITEACCESS record

  Legacy.SaveAs('archive-1999-stamped.xls');
end;

یک تداخل نامگذاری باعث سردرگمی مکرر می‌شود. ویژگی Comments در سطح سند در اینجا همان یادداشت متنی ساده‌ای است که در کادر گفتگوی ویژگی‌های فایل نشان داده می‌شود. این ویژگی هیچ ارتباطی با کامنت‌های سلول (cell comments) ندارد که اشیاء لایه گرافیکی متصل به محدوده‌ها از طریق یک API کاملاً جداگانه هستند. بررسی کدی که ادعای "ما در حال حاضر کامنت‌ها را می‌نویسیم" را بدون بررسی اینکه کدام یک منظور است می‌پذیرد، ادعایی را در مورد ویژگی اشتباه پذیرفته است، و این اتفاق بیش از آنچه نام مشترک نشان می‌دهد رخ می‌دهد. این دو فقط در نام مشترک هستند و حتی یک بایت حافظه مشترک ندارند

خواندن متادیتا در زمان ورود، و شکاف جستجو

خواندن متقارن است. پس از متد Open، همان ویژگی‌ها به صورت پرشده از فایل بازمی‌گردند، که کار بررسی متادیتای کتاب‌های کاری ورودی را به یک حلقه کوتاه تبدیل می‌کند

var
  Book: TXLSXWorkbook;
begin
  Book := TXLSXWorkbook.Create;
  try
    if Book.Open(FileName) = 1 then
    begin
      Writeln(Format('%s | title="%s" author="%s" created=%s',
        [ExtractFileName(FileName), Book.Title, Book.Author,
         FormatDateTime('yyyy-mm-dd', Book.Created)]));
      if Book.Created = 0 then
        Writeln('  no creation date recorded');
    end;
  finally
    Book.Free;
  end;
end;

در حین انجام کار برای یک محدودیت برنامه‌ریزی کنید. هیچ جستجوی فقط مخصوص ویژگی‌ها وجود ندارد. متد GetSheetNames می‌تواند برگه‌ها را بدون بارگذاری کتاب کار لیست کند، اما خواندن Title یا Author به معنای یک Open کامل است، بنابراین دسته‌بندی متادیتا در یک آرشیو بزرگ، هزینه تجزیه کامل را برای هر فایل به همراه دارد. در سمت BIFF می‌توانید این هزینه را برای حسابرسی‌های فقط‌خواندنی با تنظیم _DisableGraphics روی true قبل از باز کردن کاهش دهید، که لایه ترسیم را به طور کامل نادیده می‌گیرد. این حالت برای حلقه‌ای مناسب است که فقط ویژگی‌ها و آمار سلول‌ها را می‌خواند، و دقیقاً در لحظه‌ای که ممکن است همان نمونه ذخیره شود اشتباه است، زیرا محتوای ترسیم نادیده گرفته شده حذف خواهد شد. هنگامی که ساختار برگه به تنهایی می‌تواند مجموعه را پیش‌فیلتر کند، که صادرات تک‌برگه‌ای نمونه بارز آن برای نادیده گرفتن است، تکنیک‌های ارزان‌قیمت در مقاله ما درباره فهرست برگه‌ها و بازرسی سبک تعداد فایل‌هایی را که به مرحله گران‌قیمت می‌رسند کاهش می‌دهد. و در کارهای ثبت انبوه، که هزاران خروجی به جای بازرسی نوشته می‌شوند، الگوهای توان عملیاتی سمت نوشتن در مقاله ما درباره نوشتن جریانی برای کارهای دسته‌ای بدون تغییر اعمال می‌شوند، زیرا تخصیص ویژگی هیچ چیز قابل اندازه‌گیری به زمان ذخیره اضافه نمی‌کند

عبور از فرمت‌ها و کنترل نشت اطلاعات

ویژگی‌ها در داخل یک رابط واحد به طور تمیز رفت و برگشت می‌کنند: یک فایل .xlsx را باز کنید، آن را ویرایش کنید، ذخیره کنید، و کل مجموعه به صورت دست‌نخورده بازمی‌گردند. عبور از فرمت‌ها جایی است که فرض یکسانی از بین می‌رود، زیرا مجموعه‌های فیلد BIFF و OOXML یک‌به‌یک مطابقت ندارند. BIFF دارای Manager است و فاقد مهر زمانی؛ OOXML دارای Category، Description، و جفت Created/Modified است. مبدلی که کورکورانه کپی می‌کند، هر آنچه فرمت مقصد نمی‌تواند نگه دارد را از دست می‌دهد، بنابراین فیلدها را به طور صریح نقشه‌برداری کنید و این نقشه‌برداری را در چک‌لیست تبدیل خود در کنار هر چیز دیگری که در طول سفر باقی نمی‌ماند قرار دهید

نشتی که ارث‌بری قالب باز می‌کند در جهت دیگری جریان دارد: اطلاعاتی که هرگز قصد ارسال آن‌ها را نداشتید. نام نویسندگان، برچسب‌های پروژه داخلی پارک شده در کلمات کلیدی، عنوان پیش‌نویس که هیچ‌کس آن را پاک نکرده است. انضباط رونویسی همه چیز در مولد بالا، کل دفاع شماست، و ارزش دارد که به روشی که یک بیگانه بررسی می‌کند، با باز کردن کادر ویژگی‌ها که هر مشتری می‌تواند به آن دسترسی داشته باشد یا با غیرفشرده کردن .xlsx و خواندن مستقیم docProps/core.xml از بسته، آن را تایید کنید. آنچه در آنجا می‌بینید دقیقاً همان چیزی است که هر اندیس‌گذار پایین‌دستی مشاهده می‌کند

همین دید پایین‌دستی دلیلی است بر اینکه چرا چند فیلد مستلزم مراقبت بیشتری نسبت به بقیه هستند. عنوان (Title)، نویسنده (Author)، کلمات کلیدی (Keywords - که به عنوان برچسب ظاهر می‌شوند)، و کامنت‌ها (Comments) یا توضیحات (Description) بیشترین وزن اندیس‌گذاری را در شیرپوینت و جستجوی ویندوز حمل می‌کنند. عنوانی که برای هر سند کاملاً متمایز است و حاوی دوره زمانی و حساب است، بیش از هر طرح نام‌گذاری پوشه‌ای به پیدا شدن سند کمک می‌کند، و هزینه آن فقط یک تخصیص در هر ذخیره است

ویژگی‌های سند ارزان‌ترین پرداخت حرفه‌ای است که یک کتاب کار تولید شده می‌تواند به همراه داشته باشد، و رایج‌ترین نقص ارسالی است وقتی که هیچ‌کس مالک آن‌ها نباشد. هر دو سطح ویژگی توضیح داده شده در اینجا متعلق به کامپوننت HotXLS هستند که آن‌ها را به طور بومی برای XLS and XLSX بدون نیاز به اتوماسیون اکسل می‌نویسد