مقال تقني

HotXLS Delphi Component: sheet listing and lightweight workbook inspection في Delphi

تشرح المقالة لماذا يكون catalog رخيص القراءة وموثوقاً، وكيف يستعمل استدعاء واحد مع .xls و.xlsx والقوالب. كما تفسر قيمة الإرجاع، واكتشاف الحاويات المشفرة قبل التوجيه، وما لا يستطيع استدعاء listing الإجابة عنه

أحيانًا يكون السؤال الوحيد الذي تحتاج روتين استقبال إجابة عنه سؤالًا بنيويًا: هل يملك هذا المصنف ورقة اسمها "Mapping"، أو كم علامة تبويب يحملها. الإجابة عن ذلك باستدعاء Open هي الطريقة المكلفة لفعل ذلك. الفتح الكامل ينفخ جدول السلاسل المشترك، ويفك ترميز كل سجل نمط، ويجتاز خلايا كل ورقة عمل، لأنه لا يملك طريقة لمعرفة أنك أردت فقط جدول المحتويات. على ملف كبير، هذا مئات الميغابايتات من التخصيصات وعدة ثوانٍ من وقت المعالج تُنفق لقراءة قائمة تشغل بضعة كيلوبايتات فقط. HotXLS، مكتبة جداول بيانات Delphi الأصلية من losLab، تمنحك تلك القائمة وحدها: GetSheetNames تعيد أسماء أوراق العمل، بترتيب المصنف، دون تجسيد خلية واحدة

GetSheetNames في HotXLS داخل Delphi يقرأ كتالوج أوراق ملف XLSX أو XLS فقط بينما يمشي الفتح الكامل على كل خلية
يجثم الفهرس في workbook.xml أو سجلات BoundSheet، فيكلف السرد بضعة كيلوبايتات بينما يتدرج الفتح الكامل مع البيانات

لماذا الفهرس رخيص القراءة

كلا صيغتَي جداول البيانات تضعان جدول محتوياتهما قرب البداية، وهذا ما يجعل استدعاء السرد سريعًا لا ذكيًا فحسب. حزمة OOXML تحفظ فهرس الأوراق في xl/workbook.xml، وهو جزء يبقى صغيرًا سواء كان المصنف يحمل عشرة صفوف أو عشرة ملايين. أما ملف ‎.xls‎ بصيغة BIFF8 فيخزّن سجلات BoundSheet الخاصة به في بداية تدفق عموميات المصنف، قبل أي بيانات خلايا. لذا فإن العمل الذي يتجنبه استدعاء السرد ليس خطأ تقريب مقابل فتح كامل. إنه معظم الملف. قراءة الفهرس تكلف نفس الحفنة من الكيلوبايتات بصرف النظر عن عدد الصفوف، بينما الفتح الكامل يتوسع مع البيانات، وعلى مصنف بعدة ميغابايتات تمتد تلك الفجوة إلى عدة رتب من الحجم في كل من البايتات المُلامَسة والذاكرة المخصَّصة

ذلك الثمن المسطّح هو الخاصية التي تستحق التصميم حولها. بوابة استقبال مبنية على GetSheetNames تتصرف بنفس الطريقة على ملف من 200 صف وملف من 200 ميغابايت، فلم يعد أبطأ ملف في دفعة يحدد وتيرة القرار بشأن ما إذا كان الملف يستحق المعالجة أصلًا

استدعاء واحد عبر ‎.xls‎ و‎.xlsx‎ وصيغ القالب

في واجهة XLS، TXLSWorkbook.GetSheetNames تقرأ أكثر من ‎.xls‎ وحده. إنها تقبل أيضًا الصيغ المبنية على الأرشيف المضغوط ‎.xlsx‎ و‎.xlsm‎ و‎.xltx‎ و‎.xltm‎، ساحبة workbook.xml فقط من الأرشيف. لإدخال ‎.xls‎ حقيقي، تفحص سجلات BoundSheet وتتوقف عند أول سجل EOF في التدفق الفرعي للعموميات، فيظل الملف الثنائي الكبير يكلف فقط كيلوبايتاته الافتتاحية. واجهة XLSX تحمل ضمانًا أهم مما يبدو للوهلة الأولى لكود خدمة طويل التشغيل: TXLSXWorkbook.GetSheetNames لا تعيد ضبط كائن المصنف ولا تملؤه، فيمكن لكائن يحمل بالفعل مستندًا مفتوحًا أن يفحص ملفات أخرى دون إزعاج الملف الذي بيده. GetODSSheetNames تطبّق النهج نفسه على حزم OpenDocument، ولكل واحد من هذه الاستدعاءات حمولة زائدة من نوع تدفق، وهذا يتيح لك فحص ملف مرفوع لم يهبط على القرص أبدًا

var
  Book: TXLSXWorkbook;
  Names: TStringList;
  I: Integer;
begin
  Names := TStringList.Create;
  Book := TXLSXWorkbook.Create;
  try
    if Book.GetSheetNames('upload-7f3a.xlsx', Names) <= 0 then
      raise Exception.Create('unreadable workbook package');
    if Names.IndexOf('Mapping') < 0 then
      raise Exception.Create('required Mapping sheet is missing');
    for I := 0 to Names.Count - 1 do
      Writeln(Format('sheet %d: %s', [I, Names[I]]));
  finally
    Book.Free;
    Names.Free;
  end;
end;

الاستدعاء نفسه يصنع مربع حوار استيراد جيدًا على سطح المكتب. اسرد الأوراق، ودع المستخدم يختار واحدة، وادفع ثمن الفتح الكامل فقط بعد أن يُتَّخذ الاختيار. مع مصنف من خمسين ورقة، الفرق مرئي: منتقٍ يظهر فورًا مقابل منتقٍ يتعثر بينما يُحمَّل الملف بأكمله خلفه

ملفات ‎.xlsm‎ المُفعَّلة للماكرو وصيغ القالب تُسرَد تمامًا كملف ‎.xlsx‎ عادي، لأن الفهرس يقع في نفس workbook.xml سواء كان vbaProject.bin راكبًا معه في الحزمة أم لا. خط أنابيب استقبال يمكنه إذن تعداد أوراق مصنف يحمل ماكرو للتوجيه، دون لمس حمولة الماكرو مطلقًا ودون فعل أي شيء قد يشغّلها، تاركًا قرار سياسة الماكرو للمرحلة التي تفتح الملف فعليًا

قراءة القيمة المُعادة دون خداع نفسك

اصطلاحات القيمة المُعادة ليست موحّدة عبر HotXLS. بعض الاستدعاءات تعيد 1 عند النجاح، وأخرى تعيد عددًا، لذا فإن الفحص الوحيد الصامد لدوال السرد هو معاملة أي قيمة صفر أو أقل على أنها فشل، مع تفريغ قائمة السلاسل. قاوم إغراء قراءة قائمة فارغة على أنها "مصنف بلا أوراق". كل من ECMA-376 ومواصفة BIFF8 يشترطان ورقة واحدة على الأقل في مصنف صالح، فصفر أسماء يعني دائمًا فشل القراءة، لا أن الملف فارغ شرعيًا أبدًا

سرد فاشل هو نفسه إشارة تستحق الاحتفاظ بها. ملف ‎.xlsx‎ يفشل في الاستدعاء هو واحد من بضعة أشياء محددة: مقطوع، أو ليس حزمة OOXML حقيقية على الإطلاق (ملفات CSV مُصدَّرة من أنظمة أخرى وموسومة خطأً تظهر هنا باستمرار)، أو حاوية مشفّرة. التفريق بين هذه هو مهمة الفحص التالي. تسجيل البايتات الأولى للملف المرفوض إلى جانب الفشل عادة ما يحوّل خيط دعم إلى رسالة واحدة

اكتشاف الحاويات المشفّرة قبل التوجيه

ملف ‎.xlsx‎ مشفّر ليس أرشيفًا مضغوطًا. إنه ملف OLE مركّب يغلّف تدفقات EncryptionInfo وEncryptedPackage، فـGetSheetNames لا تستطيع الرؤية داخله وتعيد فشلًا مثل أي ملف آخر غير قابل للقراءة. CanReadEncrypted تختبر شكل تلك الحاوية، وهذا يتيح للاستقبال توجيه ملف مشفّر عن قصد بدلًا من ابتلاع خطأ قراءة عام من مكان عميق داخل عملية عامل:

تدفق فرز استلام في Delphi مع CanReadEncrypted و GetSheetNames في HotXLS يوجه الرفوع إلى يحتاج-كلمة-مرور أو غير قابل للقراءة أو عادي
يجري CanReadEncrypted أولاً لأن الملف المشفر OOXML وعاء OLE لا ترى استدعاءات السرد داخله
type
  TIntakeRoute = (irNormal, irNeedsPassword, irUnreadable);

function ClassifyUpload(const FileName: string; Names: TStrings): TIntakeRoute;
var
  Book: TXLSXWorkbook;
begin
  Book := TXLSXWorkbook.Create;
  try
    // OOXML المشفّر حاوية OLE، لا أرشيف مضغوط: تحقق أولًا،
    // لأن استدعاءات السرد لا تستطيع النظر داخله.
    if Book.CanReadEncrypted(FileName) then
      Exit(irNeedsPassword);
    if SameText(ExtractFileExt(FileName), '.ods') then
    begin
      if Book.GetODSSheetNames(FileName, Names) <= 0 then
        Exit(irUnreadable);
    end
    else if Book.GetSheetNames(FileName, Names) <= 0 then
      Exit(irUnreadable);
    Result := irNormal;
  finally
    Book.Free;
  end;
end;

التشفير هو حيث يكون HotXLS غير متماثل عمدًا، لذا يجب أن يحترم التوجيه ذلك. تشفير ‎.xls‎ القديم (RC4، وRC4 CryptoAPI، وXOR) قابل للقراءة: TXLSWorkbook.Open(FileName, Password) يفك التشفير بكلمة مرور مخزَّنة، ويمكن لتلك الملفات البقاء على المسار الآلي. أما حزم OOXML المشفّرة فتسير في الاتجاه الآخر. يستطيع HotXLS كتابة واحدة عبر SaveAsEncrypted، لكنه لا يستطيع قراءتها مجددًا. OpenEncrypted تثير EXlsxEncryptionNotImplemented عند تسليمها حزمة مشفّرة، وهذا سبب أن تصميم استقبال صادق يرسل ‎.xlsx‎ المشفّر إلى شخص لديه Excel ويبقي ‎.xls‎ الحامل لكلمة المرور داخل الكود

لعمل الدُفعات، هذا المصنِّف يكسب مكانته بتشغيله عبر مجلد وارد بأكمله قبل أن يبدأ أي عامل معالجة حقيقية، لأن كل فحص يكلف تقريبًا فتح ملف واحد وبضعة كيلوبايتات من القراءات. تحميله مقدَّمًا يغيّر نمط الفشل الذي يهم العمليات فعلًا. بدلًا من مهمة الساعة الثالثة صباحًا تموت عند الملف 412 من 600، تحصل على 412 ملفًا في قائمة الانتظار و5 مرفوضة عند الاستقبال مع سبب مرفق بكل واحد. نفس استدعاءات المكتبة، قصة تشغيلية أفضل بكثير

الأسئلة التي لا يستطيع استدعاء السرد الإجابة عنها

الأسماء والترتيب هما كل ما تحصل عليه. استدعاءات السرد لا تقول شيئًا عن الرؤية، فالأوراق المخفية والمخفية جدًا تصل في القائمة وكأنها أي ورقة أخرى. لا تُبلغ عن أبعاد النطاق المستخدم، ولا عدد الخلايا، ولا خصائص المستند. جزء docProps/core.xml صغير أيضًا، لكن لا يوجد فحص خاص بالخصائص فقط اليوم، فبيانات المؤلف والعنوان الوصفية لا تزال تكلف Open كاملًا. الطريقة النظيفة للتعايش مع ذلك هي ترك الحقائق الرخيصة توجّه كل ملف وحجز الحقائق المكلفة للملفات التي تنجو من التوجيه. للملفات التي تمضي فعلًا إلى قراءة عميقة، فحص للقراءة فقط لملف ‎.xls‎ كبير يعمل أسرع بشكل ملحوظ مع _DisableGraphics := True، التي تتخطى تحليل OfficeArt. فقط لا تحفظ أبدًا من ذلك الكائن: طبقة الرسم التي تخطّاها اختفت من النموذج، والحفظ سيُسقطها من الملف

الملفات التي تجتاز الفرز عادة ما تتجه إلى تحليل أعمق. منصة تدقيق المصنفات وتحويلها تغطي العدادات لكل ورقة التي تستحق الجمع بمجرد أن يصبح الفتح الكامل مبررًا، ودليل أداء المصنفات الكبيرة يغطي إبقاء ذلك الفتح الكامل سريعًا

HotXLS مكتبة جداول بيانات Object Pascal أصلية لـDelphi وC++Builder؛ سطح API الكامل، بما في ذلك استدعاءات الفحص المعروضة هنا، موثَّق على صفحة منتج HotXLS Delphi Component