مقال تقني

HotXLS Delphi Component: workbook audit and conversion workbench في Delphi

تتعامل النسخة المحدثة مع التحويل كقرار مخاطر: افحص أسماء الأوراق والتشفير قبل التحميل، واحسب مكونات المصنف، ووحّد return codes، ثم استخدم مصفوفة التحويل لمعرفة أين تضيع البيانات. كما تشرح أن SaveXLSWorkbookAsXLSX جسر بيانات لا جسر تخطيط

مهمة توحيد جداول بيانات على نطاق واسع هي في الحقيقة ثلاث مشكلات ترتدي معطفا واحدا لديك أرشيف بصيغ مختلطة: ملفات .xls من حقبة BIFF، وملفات .xlsx حديثة، وحفنة من ملفات .ods من تجربة LibreOffice قديمة، وعدد من الملفات لا يستطيع أحد فتحها لأن كلمة المرور رحلت مع موظف سابق الهدف هو تحويل كل شيء إلى XLSX وCSV والنسخة التي يكتبها معظم الناس من هذه المهمة هي حلقة تفتح كل ملف وتحفظه بامتداد جديد، وتعمل بلا مشكلة إلى أن يسأل أحدهم أي الملفات فقدت مخططاتها، أو أسقطت وحداتها الماكرو، أو لم تُفتح أصلا الحلقة لا تملك إجابة، لأن التحويل وحده لا يترك سجلا مقارنة الأداء الحقيقي يقوم على ثلاث مراحل: الجرد أولا، والتحويل ثانيا، والتحقق ثالثا، وعلى المراحل الثلاث أن تتشارك المعلومات كي يكون أي منها موثوقا

مخطط مسار لمنصة عمل تحويل بالتدقيق أولًا في HotXLS داخل Delphi: يُجرى جرد لأرشيف مختلط من ملفات xls و xlsx و ods، وتحويل حسب المسار، ثم تحقق مقابل الأرقام السابقة المسجلة أثناء الجرد
يحوّل منضدة العمل في ثلاث مراحل، وتصبح عدادات التدقيق المسجلة أثناء الجرد الأعدادَ السابقة التي يقارن معها التحقق

بناء بيئة عمل كهذه في Delphi أو C++Builder يعني ربط أربع قدرات في HotXLS، لا يحتاج أي منها إلى تثبيت Excel في أي مكان من خط الأنابيب هناك محركان أصليان: واجهة BIFF8 لملفات .xls وواجهة OOXML لملفات .xlsx و.ods وهناك استدعاءات فحص رخيصة تقرأ البيانات الوصفية دون تحليل الملف كاملا وهناك عدادات تدقيق لكل ورقة تخبرك بما يحويه المصنف فعليا وهناك مصفوفة تحويل بملف دقة موثق لكل مسار العمل الحقيقي هو معرفة أين تكمن الحافة الحادة في كل واحدة منها، لأن كل واحدة منها تملك حافة، وهذه الحواف بالضبط ما يحوّل دفعة ليلية نظيفة إلى حادثة صباح الاثنين

افحص قبل التحميل: أسماء الأوراق وكشف التشفير

فتح مصنف حجمه 200 ميغابايت لتكتشف أنه مشفر يهدر دقائق لكل ملف، وإذا ضربت ذلك في أرشيف كبير فإنه يهدر أياما كلتا الواجهتين تعرضان GetSheetNames، التي تقرأ البيانات الوصفية للأوراق دون ملء المصنف تنفيذ BIFF يفحص فقط سجلات BoundSheet في مقدمة الدفق؛ وتنفيذ OOXML يقرأ فقط ملف workbook.xml داخل الأرشيف المضغوط وإلى جانبها، تكشف CanReadEncrypted وجود حاوية تشفير دون محاولة فك التشفير

var
  Probe: TXLSXWorkbook;
  Names: TStringList;
begin
  Names := TStringList.Create;
  Probe := TXLSXWorkbook.Create;
  try
    if Probe.CanReadEncrypted(FileName) then
    begin
      Writeln(FileName + ': encrypted container - route to manual handling');
      Exit;
    end;
    if Probe.GetSheetNames(FileName, Names) <= 0 then
      Writeln(FileName + ': unreadable - quarantine')
    else
      Writeln(Format('%s: %d sheet(s), first "%s"',
        [FileName, Names.Count, Names[0]]));
  finally
    Probe.Free;
    Names.Free;
  end;
end;

تفصيلان تشغيليان يجعلان هذه الحلقة رخيصة GetSheetNames لا تعيد ضبط المصنف ولا تملؤه، لذا يستطيع كائن فحص واحد تصنيف آلاف الملفات دون إعادة إنشائه ونسخة هذا الاستدعاء في واجهة XLS تفهم أيضا حزم .xlsx، ما يجعلها فحصا موحدا مريحا حين لا يمكن الوثوق بامتدادات الملفات، وهو أمر نادرا ما يمكن الوثوق به في أرشيف بذلك القدم الفرز قبل التحميل يستحق معالجة خاصة به؛ آليات الفحص الخفيف موجودة في مقالتنا عن سرد الأوراق والفحص الخفيف للمصنفات

مخطط انسيابي للفرز لدفعات مصنفات HotXLS داخل Delphi: يوجه CanReadEncrypted الحاويات المشفرة إلى المعالجة اليدوية، ويعزل GetSheetNames الملفات غير القابلة للقراءة، وتدخل الملفات المجتازة تمريرة التدقيق التي تقرر مسار التحويل
يصنف الفحص بـ CanReadEncrypted وGetSheetNames كل ملف قبل التحميل، فلا تبلغ حلقة التحويل أبداً مصنفات مشفرة أو غير مقروءة

عدّ ما يحويه المصنف فعليا

بعد أن يجتاز الملف الفرز، يقرر تمرير التدقيق مسار التحويل واجهة XLSX تعرض عدادا لكل عائلة ميزات تؤثر في قرار الدقة: الخلايا المدمجة، والمخططات، والصور، وتنسيقات الشرط، والتحقق من صحة البيانات، والجداول، والروابط التشعبية، والتعليقات، إضافة إلى أعلام على مستوى المصنف للوحدات الماكرو والحماية والصيغة المصدر مسار التحويل لملف ما يعتمد تقريبا كليا على أي من هذه القيم يعود غير صفري

var
  Book: TXLSXWorkbook;
  Sheet: TXLSXWorksheet;
  I: Integer;
begin
  Book := TXLSXWorkbook.Create;
  try
    if Book.Open(FileName) <> 1 then Exit;
    for I := 0 to Book.Sheets.Count - 1 do
    begin
      Sheet := Book.Sheets[I];
      Writeln(Format('%s: cells=%d merges=%d charts=%d cf=%d dv=%d protected=%s',
        [Sheet.Name, Sheet.Cells.Count, Sheet.MergedCells.Count,
         Sheet.Charts.Count, Sheet.ConditionalFormats.Count,
         Sheet.DataValidations.Count, BoolToStr(Sheet.IsProtected, True)]));
    end;
    if Book.HasVbaProject then
      Writeln('  contains VBA project - macro policy applies');
    if Book.ExternalLinks.Count > 0 then
      Writeln(Format('  %d external link(s)', [Book.ExternalLinks.Count]));
  finally
    Book.Free;
  end;
end;

اقرأ Cells.Count مع أخذ تحفظ واحد بعين الاعتبار مخزن الخلايا متناثر، لذا يعدّ الرقم الخلايا الفعلية فقط، لا مساحة المستطيل المستخدم ورقة فيها قيمة واحدة في A1 وأخرى في ZZ9999 تُبلّغ عن خليتين، لا عن المليون خلية تقريبا التي تقع بينهما الفحص المكافئ في جانب BIFF يستخدم حدود UsedRange مع ForEachCell، ويحمل خطأ الإزاحة بواحد الذي يوقع الجميع تقريبا أول مرة: UsedRange.FirstRow ونظائرها تبدأ من صفر، بينما Cells.Item[Row, Col] يبدأ من واحد اجتياز ينسى إضافة واحد إلى كل حد يدقق المستطيل الخطأ ولا يخبرك بذلك أبدا

رافعتان تخفضان تكلفة تمرير تدقيق فقط على ملفات قديمة كبيرة ضبط _DisableGraphics إلى صحيح قبل فتح ملف .xls يتخطى تحليل طبقة الرسم OfficeArt كليا، ما يوفر وقتا حقيقيا على المصنفات الكثيفة بالأشكال لكنه تحسين للقراءة فقط: الحفظ من نسخة فُتحت بهذه الطريقة سيُسقط الرسوم التي لم تُحلَّل قط، لذا يخص هذا العلم فقط المسارات التي لن تكتب الملف مجددا أبدا وحين يحتاج التدقيق إلى محتوى كل خلية بدل العدادات، يجتاز رد النداء ForEachCell الخلايا المملوءة مباشرة ويتجنب عبء Variant لكل وصول الذي تدفعه خصائص الخلايا المفهرسة عند كل قراءة، وهو ما يتراكم بسرعة عبر ملايين الخلايا

وحّد رموز الإرجاع غير المتسقة مبكرا

تُبلّغ استدعاءات الإدخال والإخراج في HotXLS عن الأخطاء عبر نتائج عددية صحيحة لا استثناءات، والقواعد ليست موحدة عبر الواجهة معظم استدعاءات الفتح والحفظ تعيد 1 عند النجاح و-1 عند الفشل GetSheetNames تعيد عدد الأوراق، أو -1 مع تفريغ القائمة SaveAsHTML في XLSX تكسر النمط مجددا وتعيد 0 عند النجاح، و-1 لفهرس ورقة خارج النطاق بيئة عمل تختبر = 1 في كل مكان ستصنّف خطأ وبصمت الاستدعاءات التي تشير إلى النجاح بطريقة أخرى، والتي تختبر <> -1 ستبتلع تلك التي تفشل برمز مختلف

القاعدة التي تصمد أمام الواجهة كاملة أضيق مما تبدو: عامل <= 0 كفشل في الاستدعاءات التي تعيد عددا، وتحقق من قيمة النجاح الموثقة لكل روتين حفظ تستخدمه فعليا، وضع الاثنين خلف دالة صغيرة واحدة للتحقق من النتيجة كي يعيش العرف في مكان واحد بالضبط خطوط الأنابيب الدفعية تفشل بسبب تراكم بطيء لرموز إرجاع غير مفحوصة أكثر بكثير مما تفشل بسبب أي خطأ محلل غريب، وثمن الخطأ هنا يظهر بعد أربعين ألف ملف، حين لا يتذكر أحد أي التحويلات نجحت فعلا

مصفوفة التحويل وأين يفقد كل طريق البيانات

تقسّم الواجهتان عمل التحويل بينهما TXLSXWorkbook تفتح XLSX وODS وCSV، وتحفظ XLSX وODS وCSV وHTML وRTF وXLSX مشفرة بـAES TXLSWorkbook تفتح وتحفظ BIFF، وتصدّر إلى HTML وRTF وCSV الأمر المفيد أن كل مسار يأتي بملف دقة موثق، لا بوعد غامض بالصحة، لذا يمكنك أن تقرر مسبقا أي المسارات آمنة لأي الملفات

تصدير CSV يكتب UTF-8 مع BOM، ونهايات أسطر CRLF، واقتباس وفق RFC 4180 ما لا يفعله هو حساب الصيغ: خلية تحمل =SUM(...) تُصدَّر كنص الصيغة الحرفي، فتتحول ورقة من الصيغ إلى ورقة من النصوص ما لم تحسب القيم أولا تصدير HTML ينتج جدولا واحدا، مع colspan وrowspan تقومان مقام الخلايا المدمجة والأنماط الأساسية مضمّنة تصدير RTF له حد أوضح: لا يمكنه امتداد الخلايا المدمجة عبر الأعمدة، لذا تخرج خلايا استمرار الدمج فارغة استيراد ODS خفيف عمدا، بحسب توثيق المكتبة نفسها القيم القياسية ونتائج الصيغ المخزنة مؤقتا تمر؛ الأنماط وتعبيرات صيغ ODF الحية والرسوم لا تمر هذا مهم في اللحظة التي يحوي فيها الأرشيف ملفات OpenDocument حقيقية تحكمها OASIS ODF 1.3، حيث يحتاج أي تحويل يقارب الأمانة البصرية أكثر مما بُني هذا المسار ليحمله، وتمرير التدقيق هو ما يخبرك بوجود هذه الملفات قبل أن تُسطّح الدفعة كل ذلك بصمت

SaveXLSWorkbookAsXLSX جسر بيانات، لا جسر تخطيط

واجهة BIFF لا تستطيع كتابة OOXML مباشرة، لذا يمر العبور من .xls إلى .xlsx عبر دالة SaveXLSWorkbookAsXLSX في وحدة lxXlsxExport دقة هذا الجسر تستحق أن تُذكر بوضوح، لأن الاسم يوحي بأكثر مما يقدم فعلا ينسخ القيم والصيغ وتنسيقات الأرقام وألوان التعبئة وخصائص الخط الأساسية وعروض الأعمدة وإعدادات العرض مثل خطوط الشبكة لا ينسخ الحدود ولا النطاقات المدمجة ولا التعليقات ولا المخططات ولا تنسيقات الشرط بالنسبة إلى التوحيد على مستوى البيانات، حيث تحلل الأنظمة اللاحقة النتيجة ولا ينظر أحد إلى التنسيق، هذا يكفي تماما ولا يُفقد شيء يحتاجه أحد أما بالنسبة إلى تقرير مجلس إدارة منسّق مخصص لأن يقرأه إنسان، فهذا لا يكفي، وهنا بالضبط تثبت عدادات التدقيق جدواها: ملف رصده التدقيق بأنه يحمل مخططات وتنسيقات شرط ينبغي أن يُوجَّه إلى طابور مراجعة يدوية، لا عبر جسر سيُسقط كليهما دون كلمة

مخطط دقة الجسر لـ SaveXLSWorkbookAsXLSX في HotXLS داخل Delphi: تعبر القيم والصيغ وتنسيقات الأعداد وألوان التعبئة وسمات الخط الأساسية وعرض الأعمدة وإعدادات العرض من BIFF xls إلى XLSX، بينما تسقط الحدود والمدى المدموجة والتعليقات والمخططات والتنسيقات الشرطية
يحمل SaveXLSWorkbookAsXLSX عبر جسر BIFF إلى OOXML البياناتَ التي يحتاجها محلل، وعدادات التدقيق هي ما يميز الملفات التي كانت ستُسقط مخططاتها ودمجاتها
var
  Legacy: IXLSWorkbook;        // مرجع واجهة: لا تستدعِ Free
  Modern: TXLSXWorkbook;
begin
  if SameText(ExtractFileExt(FileName), '.xls') then
  begin
    Legacy := TXLSWorkbook.Create;
    if Legacy.Open(FileName) <= 0 then Exit;
    if SaveXLSWorkbookAsXLSX(Legacy,
         ChangeFileExt(FileName, '.xlsx')) <= 0 then
      Writeln('bridge failed: ' + FileName);
  end
  else
  begin
    Modern := TXLSXWorkbook.Create;
    try
      Modern.StreamingWrite := True;     // بث XML للورقة مباشرة إلى الأرشيف المضغوط
      if Modern.Open(FileName) = 1 then
        Modern.SaveAsCSV(ChangeFileExt(FileName, '.csv'), 0, ',');
    finally
      Modern.Free;
    end;
  end;
end;

الحلقة أعلاه تُظهر أيضا رافعة الإنتاجية في جانب OOXML ضبط StreamingWrite إلى صحيح يبث XML ورقة العمل مباشرة إلى الحزمة الناتجة بدل تجهيزه كسلسلة نصية عملاقة واحدة في الذاكرة، وهو الفارق بين تشغيل مريح وانهيار بسبب نفاد الذاكرة عند وصول الملفات إلى مئات آلاف الصفوف الحجم وسلوك الذاكرة لذلك الوضع يحظيان بمعالجة خاصة بهما في مقالتنا عن الكتابة المتدفقة لمهام الدفعات على الخادم خاصية أخرى مهمة لدفعة تريد استخدام كل نواة: لا واحدة من الواجهتين آمنة للخيوط، لكن ولا واحدة منهما تشارك حالة عامة أيضا، لذا النمط المدعوم للتحويل المتوازي هو نسخة مصنف واحدة لكل خيط عامل، دون أي قفل بينها

الملفات المحمية بكلمة مرور، وما ينبغي فعله بها

الملفات المقفلة في الأرشيف تنقسم بوضوح حسب الصيغة، والانقسام يقرر إلى أين تذهب تشفير .xls القديم، سواء كان RC4، أو RC4 عبر CryptoAPI، أو تشويش XOR القديم، قابل للقراءة: مرّر كلمة المرور إلى Open ويتحول الملف كأي ملف آخر حزم .xlsx المشفرة قصة مختلفة HotXLS تكشفها بواسطة CanReadEncrypted لكنها لا تستطيع فك تشفيرها، لذا الخطوة الصادقة الوحيدة هي توجيهها إلى طابور يفتح فيه إنسان كل ملف ويعيد حفظه في Excel قبل أن ينضم إلى خط الأنابيب مجددا هذا التفاوت يستحق التخطيط له مسبقا، لأن ملفات XLSX المشفرة هي على الأرجح أكثر الملفات التي يهتم بها أحد فعلا

إغلاق الحلقة بالتحقق

المرحلة الثالثة هي التي يُستغنى عنها غالبا، وتجاهلها هو ما يحوّل تحويلا جماعيا إلى مصدر مسؤولية لا مسار حفظ في HotXLS يحسب الصيغ Excel يعيد الحساب عند فتح الملف، لذا تحويل XLSX إلى XLSX يبقى صحيحا، لكن هدف CSV يستقبل نص الصيغة حرفيا ما لم يشغّل خط الأنابيب أولا Calculate على الخلايا ويكتب النتائج مرة أخرى معرفة ذلك مسبقا هي الفارق بين CSV مليء بالأرقام وCSV مليء بسلاسل =SUM(...) لا يلاحظها أحد حتى يختنق استيراد لاحق بها

التحقق نفسه رخيص بما يكفي بحيث لا عذر لتجاهله أعد فتح كل ملف محوَّل بالمكتبة نفسها، وأعد تشغيل عدادات التدقيق، وقارنها بالأرقام السابقة للتحويل التي سجّلها تمرير الجرد بالفعل عدد أوراق انخفض، أو عدد مخططات هبط إلى صفر بينما كان المصدر يحمل ثلاثة، أو عدد خلايا سقط فجأة: كل واحدة خسارة صامتة تُكتشف بثمن فتح ثانٍ افحص عينة بالعين في Excel أو LibreOffice فوق ذلك، والمزيج يلتقط الغالبية الساحقة من أضرار التحويل قبل شحنها هذا هو السبب الكامل وراء تغذية مرحلة الجرد لمرحلة التحقق دون الأرقام السابقة، لا تثبت الأرقام اللاحقة شيئا

بيئة عمل تبدأ بالتدقيق تحوّل تحويلا جماعيا محفوفا بالمخاطر إلى عملية قابلة للقياس بممر حجر صحي للملفات التي لا يمكنها الاجتياز بنظافة كل استدعاءات الفحص والعد والتحويل الموضحة هنا جزء من HotXLS Delphi Component، الذي يشغّلها أصليا داخل العملية دون أتمتة Excel