مقدمة

عندما يحتاج عملك إلى استيعاب دفعات كبيرة من الفواتير أو المستندات القانونية أو تصديرات البريد الإلكتروني التي تصل كملفات ZIP أو RAR مضغوطة، يكون النهج التقليدي هو فك ضغطها إلى القرص، فتح كل ملف بقارئ منفصل، ثم حذف الملفات المؤقتة. تُضيف هذه الدورة إReading/​Writing إضافية مُكلفة، وتعقّد عملية التنظيف، وتجعل التعامل مع الأرشيفات المتداخلة كابوسًا.

يُزيل GroupDocs.Parser لـ .NET تلك النقاط المؤلمة. فهو يتيح لك فتح الأرشيف مباشرةً، تعداد كل الإدخالات، واستخراج النص الخام (وبيانات التعريف) بالكامل في الذاكرة. في هذه المقالة ستتعلم كيفية:

  • تثبيت حزمة NuGet الخاصة بـ Parser.
  • سحب النص من أرشيف مسطح في تمريرة واحدة.
  • السير بشكل متكرر عبر ملفات ZIP/RAR المتداخلة.
  • تطبيق إعدادات أفضل الممارسات للمعالجة المتينة.

لماذا يُعد تحليل الأرشيف في الذاكرة مهمًا

معالجة الأرشيفات في الذاكرة توفر لك:

  • عدم وجود ملفات مؤقتة – لا فوضى على القرص، ولا ملفات متبقية.
  • السرعة – تجنّب دورة القراءة/الكتابة الإضافية لكل إدخال.
  • القابلية للتوسع – التعامل مع الأرشيفات الكبيرة أو التدفقات السحابية حيث قد لا يتوفر نظام ملفات.

المتطلبات المسبقة

  • .NET 6.0 أو أحدث.
  • GroupDocs.Parser لـ .NET (الإصدار الأحدث) – راجع الرخصة المؤقتة لتقييم مجاني.
  • أرشيف ZIP أو RAR يحتوي على مستندات مدعومة (PDF، DOCX، TXT، إلخ).

التثبيت

dotnet add package GroupDocs.Parser

أضف الأسماء المكانية المطلوبة:

using GroupDocs.Parser;
using GroupDocs.Parser.Data;
using System.Collections.Generic;
using System.IO;

الخطوة 1 – فتح الأرشيف

الخطوة الأولى هي إنشاء كائن Parser يشير إلى ملف الأرشيف. تُعيد GetContainer() مجموعة من كائنات ContainerItem – واحدة لكل إدخال داخل الأرشيف.

// مسار الأرشيف الذي تريد فحصه
string archivePath = "./SampleDocs/InvoicesArchive.zip";

using (Parser parser = new Parser(archivePath))
{
    // استرجاع كل ملف (أو أرشيف متداخل) داخل الحاوية
    IEnumerable<ContainerItem> attachments = parser.GetContainer();

    if (attachments == null)
    {
        Console.WriteLine("الأرشيف فارغ أو لا يمكن قراءته.");
        return;
    }

    // تمرير المجموعة إلى مساعد يستخرج النص/بيانات التعريف
    ExtractDataFromAttachments(attachments);
}

ما يحدث هنا:

  • يقوم مُنشئ Parser بتحميل الأرشيف دون استخراجه إلى القرص.
  • GetContainer() يقرأ دليل الأرشيف بشكل كسول ويعطيك كائنات ContainerItem يمكنك العمل معها.

الخطوة 2 – معالجة كل إدخال

ExtractDataFromAttachments يتجول في قائمة ContainerItem، يطبع بيانات تعريف أساسية، يكتشف الأرشيفات المتداخلة، ويستخرج النص من المستندات العادية. الطريقة قابلة لإعادة الاستخدام بالكامل – استدعها مرةً للأرشيف العلوي ومرةً أخرى لأي أرشيف متداخل تكتشفه.

/// <summary>
/// يستخرج بشكل متكرر بيانات التعريف والنص العادي من كل عنصر في الأرشيف.
/// </summary>
static void ExtractDataFromAttachments(IEnumerable<ContainerItem> attachments)
{
    foreach (ContainerItem item in attachments)
    {
        // طباعة سطر سريع يتضمن اسم الملف وحجمه (اختياري)
        Console.WriteLine($"File: {item.FilePath} | Size: {item.Metadata.Size} bytes");

        try
        {
            // كل ContainerItem يمكنه فتح مثيل Parser خاص به
            using (Parser itemParser = item.OpenParser())
            {
                if (itemParser == null)
                {
                    // العنصر ليس مستندًا مدعومًا – تجاهله
                    continue;
                }

                // اكتشاف الأرشيفات المتداخلة عبر الامتداد (بدون حساسية لحالة الأحرف)
                bool isArchive = item.FilePath.EndsWith(".zip", StringComparison.OrdinalIgnoreCase) ||
                                 item.FilePath.EndsWith(".rar", StringComparison.OrdinalIgnoreCase);

                if (isArchive)
                {
                    // معالجة الأرشيف الداخلي بشكل متكرر
                    IEnumerable<ContainerItem>? nested = itemParser.GetContainer();
                    if (nested != null)
                    {
                        ExtractDataFromAttachments(nested);
                    }
                }
                else
                {
                    // مستند عادي – استخرج نصه الخام
                    using (TextReader reader = itemParser.GetText())
                    {
                        string text = reader.ReadToEnd();
                        Console.WriteLine($"Extracted {text.Length} characters from {item.FilePath}");
                        // هنا يمكنك تخزين `text` في قاعدة بيانات، فهرستها، إلخ.
                    }
                }
            }
        }
        catch (UnsupportedDocumentFormatException)
        {
            // نوع الملف غير مدعوم من قبل GroupDocs.Parser – تجاهله بهدوء
            Console.WriteLine($"Skipping unsupported format: {item.FilePath}");
        }
    }
}

نقاط رئيسية

  • الوصول إلى بيانات التعريفitem.Metadata يعطك اسم الملف، حجمه، تاريخ الإنشاء، إلخ، دون قراءة محتوى الملف.
  • المعالجة المتكررة – تستدعي الطريقة نفسها نفسها عندما تصادف ZIP/RAR آخر، ما يمنحك دعمًا غير محدود للتداخل.
  • المرونة في التعامل مع الأخطاء – يتم الإمساك بـ UnsupportedDocumentFormatException بحيث لا يؤدي ملف واحد سيء إلى إيقاف التنفيذ بالكامل.

الخطوة 3 – تجميع كل شيء معًا

فيما يلي برنامج بسيط يمكن نسخه ولصقه يجمع المقطعين السابقين. يوضح تدفقًا كاملاً من البداية إلى النهاية: تثبيت، فتح، معالجة، وتقرير.

using GroupDocs.Parser;
using GroupDocs.Parser.Data;
using System;
using System.Collections.Generic;
using System.IO;

class ArchiveTextExtractor
{
    static void Main(string[] args)
    {
        string archivePath = args.Length > 0 ? args[0] : "./SampleDocs/InvoicesArchive.zip";
        using (Parser parser = new Parser(archivePath))
        {
            IEnumerable<ContainerItem> attachments = parser.GetContainer();
            if (attachments == null)
            {
                Console.WriteLine("No items found in the archive.");
                return;
            }
            ExtractDataFromAttachments(attachments);
        }
    }

    static void ExtractDataFromAttachments(IEnumerable<ContainerItem> attachments)
    {
        foreach (ContainerItem item in attachments)
        {
            Console.WriteLine($"File: {item.FilePath} | Size: {item.Metadata.Size} bytes");
            try
            {
                using (Parser itemParser = item.OpenParser())
                {
                    if (itemParser == null) continue;

                    bool isArchive = item.FilePath.EndsWith(".zip", StringComparison.OrdinalIgnoreCase) ||
                                     item.FilePath.EndsWith(".rar", StringComparison.OrdinalIgnoreCase);

                    if (isArchive)
                    {
                        var nested = itemParser.GetContainer();
                        if (nested != null) ExtractDataFromAttachments(nested);
                    }
                    else
                    {
                        using (TextReader reader = itemParser.GetText())
                        {
                            string text = reader.ReadToEnd();
                            Console.WriteLine($"Extracted {text.Length} chars from {item.FilePath}");
                        }
                    }
                }
            }
            catch (UnsupportedDocumentFormatException)
            {
                Console.WriteLine($"Unsupported format: {item.FilePath}");
            }
        }
    }
}

شغّل البرنامج مع مسار الأرشيف الخاص بك:

dotnet run -- ./Data/LegalDocs.zip

أفضل الممارسات والنصائح

  • حصر خيارات التحليل – بشكل افتراضي يستخرج Parser كل المحتوى المدعوم. إذا كنت تحتاج النص فقط، تجنب استدعاء طرق ثقيلة إضافية مثل GetImages().
  • الأرشيفات الكبيرة – عالج العناصر تسلسليًا كما هو موضح؛ تجنّب تحميل كل النصوص في الذاكرة مرةً واحدة.
  • الأداء – تخطّى الأرشيفات المتداخلة التي لا تحتاجها عبر فحص امتداد الملف قبل التكرار.
  • معالجة الأخطاء – احرص دائمًا على الإمساك بـ UnsupportedDocumentFormatException؛ فالكثير من الأرشيفات المؤسسية تحتوي على ملفات ثنائية لا يستطيع المحلل قراءتها.

الخلاصة

يقدّم GroupDocs.Parser لـ .NET طريقة نظيفة وفي الذاكرة لقراءة كل مستند داخل أرشيفات ZIP أو RAR، مهما كان عمق تداخله. ببضع أسطر من الشيفرة يمكنك استبدال خطوط أنابيب فك‑ضغط + تحليل المعقدة، تقليل عبء الـ I/O، وبناء خدمات استيعاب مستندات موثوقة.

الخطوات التالية

موارد إضافية