استخراج متن از اسناد MS Word در سی شارپ

استخراج متن از اسناد Word اغلب در سناریوهای مختلف انجام می شود. به عنوان مثال، برای تجزیه و تحلیل متن، استخراج بخش های خاصی از یک سند و ترکیب آنها در یک سند واحد و غیره. در این مقاله با نحوه استخراج متن از اسناد ورد به صورت برنامه نویسی در سی شارپ آشنا می شوید. علاوه بر این، نحوه استخراج محتوا بین عناصر خاص مانند پاراگراف ها، جداول و غیره را به صورت پویا توضیح خواهیم داد.

کتابخانه سی شارپ برای استخراج متن از اسناد Word

Aspose.Words for .NET یک کتابخانه قدرتمند است که به شما امکان می دهد اسناد MS Word را از ابتدا ایجاد کنید. علاوه بر این، به شما امکان می دهد اسناد Word موجود را برای رمزگذاری، تبدیل، استخراج متن و غیره دستکاری کنید. ما از این کتابخانه برای استخراج متن از اسناد Word DOCX یا DOC استفاده خواهیم کرد. می‌توانید DLL API را دانلود یا مستقیماً از NuGet با استفاده از کنسول مدیریت بسته نصب کنید.

PM> Install-Package Aspose.Words

استخراج متن در اسناد Word با استفاده از سی شارپ

یک سند MS Word از عناصر مختلفی تشکیل شده است که شامل پاراگراف ها، جداول، تصاویر و غیره می شود. بنابراین، الزامات استخراج متن می تواند از سناریویی به سناریوی دیگر متفاوت باشد. به عنوان مثال، ممکن است لازم باشد متنی را بین پاراگراف ها، نشانک ها، نظرات و غیره استخراج کنید.

هر نوع عنصر در یک سند Word به عنوان یک گره نشان داده می شود. بنابراین، برای پردازش یک سند، باید با گره ها بازی کنید. پس بیایید شروع کنیم و نحوه استخراج متن از اسناد Word را در سناریوهای مختلف ببینیم.

استخراج متن از یک سند Word در سی شارپ

در این بخش قصد داریم یک استخراج کننده متن سی شارپ برای اسناد Word پیاده سازی کنیم و گردش کار استخراج متن به صورت زیر خواهد بود:

  • ابتدا گره هایی را که می خواهیم در فرآیند استخراج متن قرار دهیم را تعریف می کنیم.
  • سپس، محتوا را بین گره های مشخص شده (شامل یا حذف گره های شروع و پایان) استخراج می کنیم.
  • در نهایت، ما از کلون گره های استخراج شده استفاده می کنیم، به عنوان مثال برای ایجاد یک سند Word جدید متشکل از محتوای استخراج شده.

اکنون متدی به نام ExtractContent می نویسیم که گره ها و برخی پارامترهای دیگر را برای انجام استخراج متن به آن پاس می دهیم. این روش سند را تجزیه و گره ها را شبیه سازی می کند. در زیر پارامترهایی هستند که به این متد ارسال می کنیم.

  1. StartNode و EndNode به ترتیب به عنوان نقطه شروع و پایان برای استخراج محتوا. این گره‌ها می‌توانند هر دو سطح بلوک (پاراگراف، جدول) یا سطح درون خطی (به عنوان مثال Run، FieldStart، BookmarkStart و غیره) باشند.
    1. برای ارسال یک فیلد باید شی FieldStart مربوطه را ارسال کنید.
    2. برای ارسال نشانک ها، گره های BookmarkStart و BookmarkEnd باید ارسال شوند.
    3. برای نظرات، گره های CommentRangeStart و CommentRangeEnd باید استفاده شوند.
  2. IsInclusive مشخص می کند که آیا نشانگرها در استخراج گنجانده شده اند یا خیر. اگر این گزینه روی false تنظیم شود و همان گره یا گره های متوالی ارسال شود، یک لیست خالی برگردانده می شود.

در زیر پیاده سازی کامل متد ExtractContent است که محتوای بین گره هایی را که ارسال می شود استخراج می کند.

public static ArrayList ExtractContent(Node startNode, Node endNode, bool isInclusive)
{
    // ابتدا بررسی کنید که گره های ارسال شده به این روش برای استفاده معتبر هستند.
    VerifyParameterNodes(startNode, endNode);

    // یک لیست برای ذخیره گره های استخراج شده ایجاد کنید.
    ArrayList nodes = new ArrayList();

    // یک رکورد از گره های اصلی ارسال شده به این روش نگه دارید تا در صورت نیاز بتوانیم گره های نشانگر را تقسیم کنیم.
    Node originalStartNode = startNode;
    Node originalEndNode = endNode;

    // استخراج محتوا بر اساس گره های سطح بلوک (پاراگراف ها و جداول). از میان گره های والد عبور کنید تا آنها را پیدا کنید.
    // بسته به خطی بودن گره های نشانگر، محتوای گره های اول و آخر را تقسیم می کنیم
    while (startNode.ParentNode.NodeType != NodeType.Body)
        startNode = startNode.ParentNode;

    while (endNode.ParentNode.NodeType != NodeType.Body)
        endNode = endNode.ParentNode;

    bool isExtracting = true;
    bool isStartingNode = true;
    bool isEndingNode = false;
    // گره فعلی که از سند استخراج می کنیم.
    Node currNode = startNode;

    // شروع به استخراج محتوا کنید. تمام گره های سطح بلوک را پردازش کنید و به طور خاص اولین و آخرین گره ها را در صورت نیاز تقسیم کنید تا قالب بندی پاراگراف حفظ شود.
    // این روش کمی پیچیده‌تر از استخراج‌کننده معمولی است، زیرا باید در استخراج با استفاده از گره‌های درون خطی، فیلدها، نشانک‌ها و غیره فاکتور بگیریم تا واقعاً مفید باشد.
    while (isExtracting)
    {
        // برای بدست آوردن یک کپی، گره فعلی و فرزندان آن را شبیه سازی کنید.
        Node cloneNode = currNode.Clone(true);
        isEndingNode = currNode.Equals(endNode);

        if ((isStartingNode || isEndingNode) && cloneNode.IsComposite)
        {
            // ما باید هر نشانگر را جداگانه پردازش کنیم، بنابراین آن را به یک روش جداگانه منتقل کنیم.
            if (isStartingNode)
            {
                ProcessMarker((CompositeNode)cloneNode, nodes, originalStartNode, isInclusive, isStartingNode, isEndingNode);
                isStartingNode = false;
            }

            // شرطی باید جدا باشد زیرا نشانگرهای شروع و پایان سطح بلوک ممکن است یک گره باشند.
            if (isEndingNode)
            {
                ProcessMarker((CompositeNode)cloneNode, nodes, originalEndNode, isInclusive, isStartingNode, isEndingNode);
                isExtracting = false;
            }
        }
        else
            // Node یک نشانگر شروع یا پایان نیست، به سادگی کپی را به لیست اضافه کنید.
            nodes.Add(cloneNode);

        // به گره بعدی بروید و آن را استخراج کنید. اگر گره بعدی تهی باشد، به این معنی است که بقیه محتوا در بخش دیگری یافت می شود.
        if (currNode.NextSibling == null && isExtracting)
        {
            // به بخش بعدی بروید.
            Section nextSection = (Section)currNode.GetAncestor(NodeType.Section).NextSibling;
            currNode = nextSection.Body.FirstChild;
        }
        else
        {
            // به گره بعدی در بدنه بروید.
            currNode = currNode.NextSibling;
        }
    }

    // گره ها را بین نشانگرهای گره برگردانید.
    return nodes;
}

برخی از روش های کمکی نیز توسط روش ExtractContent برای انجام عملیات استخراج متن مورد نیاز است که در زیر آورده شده است.

public static List<Paragraph> ParagraphsByStyleName(Document doc, string styleName)
{
    // یک آرایه برای جمع آوری پاراگراف های سبک مشخص شده ایجاد کنید.
    List<Paragraph> paragraphsWithStyle = new List<Paragraph>();

    NodeCollection paragraphs = doc.GetChildNodes(NodeType.Paragraph, true);

    // تمام پاراگراف ها را جستجو کنید تا آنهایی را که دارای سبک مشخص شده هستند بیابید.
    foreach (Paragraph paragraph in paragraphs)
    {
        if (paragraph.ParagraphFormat.Style.Name == styleName)
            paragraphsWithStyle.Add(paragraph);
    }

    return paragraphsWithStyle;
}
private static void VerifyParameterNodes(Node startNode, Node endNode)
{
    // ترتیب انجام این بررسی ها مهم است.
    if (startNode == null)
        throw new ArgumentException("Start node cannot be null");
    if (endNode == null)
        throw new ArgumentException("End node cannot be null");

    if (!startNode.Document.Equals(endNode.Document))
        throw new ArgumentException("Start node and end node must belong to the same document");

    if (startNode.GetAncestor(NodeType.Body) == null || endNode.GetAncestor(NodeType.Body) == null)
        throw new ArgumentException("Start node and end node must be a child or descendant of a body");

    // بررسی کنید که گره پایانی بعد از گره شروع در درخت DOM باشد
    // ابتدا بررسی کنید که آیا آنها در بخش های مختلف هستند، سپس اگر موقعیت خود را در بدنه همان قسمتی که در آن قرار دارند بررسی کنید.
    Section startSection = (Section)startNode.GetAncestor(NodeType.Section);
    Section endSection = (Section)endNode.GetAncestor(NodeType.Section);

    int startIndex = startSection.ParentNode.IndexOf(startSection);
    int endIndex = endSection.ParentNode.IndexOf(endSection);

    if (startIndex == endIndex)
    {
        if (startSection.Body.IndexOf(startNode) > endSection.Body.IndexOf(endNode))
            throw new ArgumentException("The end node must be after the start node in the body");
    }
    else if (startIndex > endIndex)
        throw new ArgumentException("The section of end node must be after the section start node");
}
private static bool IsInline(Node node)
{
    // تست کنید که آیا گره از نود پاراگراف یا جدول است و همچنین پاراگراف یا جدول نیست، یک پاراگراف در داخل یک کلاس نظر که پس از یک پاراف است امکان پذیر است.
    return ((node.GetAncestor(NodeType.Paragraph) != null || node.GetAncestor(NodeType.Table) != null) && !(node.NodeType == NodeType.Paragraph || node.NodeType == NodeType.Table));
}
private static void ProcessMarker(CompositeNode cloneNode, ArrayList nodes, Node node, bool isInclusive, bool isStartMarker, bool isEndMarker)
{
    // اگر با یک گره سطح بلوک سروکار داریم، فقط ببینید که آیا باید در آن گنجانده شود و آن را به لیست اضافه کنید.
    if (!IsInline(node))
    {
        // اگر نشانگرها همان گره هستند، گره را دوبار اضافه نکنید
        if (!(isStartMarker && isEndMarker))
        {
            if (isInclusive)
                nodes.Add(cloneNode);
        }
        return;
    }

    // اگر یک نشانگر یک گره FieldStart است، بررسی کنید که آیا قرار است شامل شود یا خیر.
    // ما برای سادگی فرض می کنیم که FieldStart و FieldEnd در یک پاراگراف ظاهر می شوند.
    if (node.NodeType == NodeType.FieldStart)
    {
        // اگر نشانگر یک گره شروع است و گنجانده نشده است، به انتهای فیلد بروید.
        // اگر نشانگر یک گره انتهایی است و قرار است اضافه شود، به فیلد انتهایی بروید تا فیلد حذف نشود.
        if ((isStartMarker && !isInclusive) || (!isStartMarker && isInclusive))
        {
            while (node.NextSibling != null && node.NodeType != NodeType.FieldEnd)
                node = node.NextSibling;

        }
    }

    // اگر هر یک از نشانگرها بخشی از یک نظر است، برای گنجاندن خود نظر باید نشانگر را به سمت نظر جلو ببریم
    // گره بعد از گره CommentRangeEnd پیدا شد.
    if (node.NodeType == NodeType.CommentRangeEnd)
    {
        while (node.NextSibling != null && node.NodeType != NodeType.Comment)
            node = node.NextSibling;

    }

    // گره مربوطه را در گره کلون شده خود با شاخص پیدا کنید و آن را برگردانید.
    // اگر گره شروع و پایان یکسان باشد برخی از گره های فرزند ممکن است قبلاً حذف شده باشند. کم کنید
    // تفاوت برای به دست آوردن شاخص مناسب.
    int indexDiff = node.ParentNode.ChildNodes.Count - cloneNode.ChildNodes.Count;

    // تعداد گره های کودک یکسان است.
    if (indexDiff == 0)
        node = cloneNode.ChildNodes[node.ParentNode.IndexOf(node)];
    else
        node = cloneNode.ChildNodes[node.ParentNode.IndexOf(node) - indexDiff];

    // گره ها را تا/از نشانگر بردارید.
    bool isSkip = false;
    bool isProcessing = true;
    bool isRemoving = isStartMarker;
    Node nextNode = cloneNode.FirstChild;

    while (isProcessing && nextNode != null)
    {
        Node currentNode = nextNode;
        isSkip = false;

        if (currentNode.Equals(node))
        {
            if (isStartMarker)
            {
                isProcessing = false;
                if (isInclusive)
                    isRemoving = false;
            }
            else
            {
                isRemoving = true;
                if (isInclusive)
                    isSkip = true;
            }
        }

        nextNode = nextNode.NextSibling;
        if (isRemoving && !isSkip)
            currentNode.Remove();
    }

    // پس از پردازش، گره مرکب ممکن است خالی شود. اگر دارد آن را درج نکنید.
    if (!(isStartMarker && isEndMarker))
    {
        if (cloneNode.HasChildNodes)
            nodes.Add(cloneNode);
    }

}
public static Document GenerateDocument(Document srcDoc, ArrayList nodes)
{
    // یک سند خالی ایجاد کنید.
    Document dstDoc = new Document();
    // پاراگراف اول را از سند خالی حذف کنید.
    dstDoc.FirstSection.Body.RemoveAllChildren();

    // هر گره را از لیست به سند جدید وارد کنید. قالب بندی اصلی گره را حفظ کنید.
    NodeImporter importer = new NodeImporter(srcDoc, dstDoc, ImportFormatMode.KeepSourceFormatting);

    foreach (Node node in nodes)
    {
        Node importNode = importer.ImportNode(node, true);
        dstDoc.FirstSection.Body.AppendChild(importNode);
    }

    // سند ایجاد شده را برگردانید.
    return dstDoc;
}

اکنون ما آماده ایم که از این روش ها استفاده کرده و متن را از یک سند Word استخراج کنیم.

متن را بین پاراگراف ها در یک سند Word استخراج کنید

بیایید نحوه استخراج محتوا بین دو پاراگراف در یک سند Word DOCX را ببینیم. در ادامه مراحل انجام این عمل در سی شارپ آمده است.

  • ابتدا سند Word را با استفاده از کلاس Document بارگذاری کنید.
  • با استفاده از روش Document.FirstSection.Body.GetChild(NodeType.PARAGRAPH، int، boolean)، مرجع پاراگراف های شروع و پایان را به دو شیء دریافت کنید.
  • متد ExtractContent (startPara, endPara, True) را برای استخراج گره ها در یک شیء فراخوانی کنید.
  • برای ایجاد سند متشکل از محتوای استخراج شده، روش کمکی GenerateDocument (Document, extractedNodes) را فراخوانی کنید.
  • در نهایت سند برگشتی را با استفاده از روش Document.Save(string) ذخیره کنید.

نمونه کد زیر نحوه استخراج متن بین پاراگراف های 7 و 11 در یک سند Word در سی شارپ را نشان می دهد.

// سند Word را بارگیری کنید
Document doc = new Document("document.docx");

// گره ها را جمع آوری کنید (روش GetChild از شاخص مبتنی بر 0 استفاده می کند)
Paragraph startPara = (Paragraph)doc.FirstSection.Body.GetChild(NodeType.Paragraph, 6, true);
Paragraph endPara = (Paragraph)doc.FirstSection.Body.GetChild(NodeType.Paragraph, 10, true);

// محتوای بین این گره ها را در سند استخراج کنید. این نشانگرها را در استخراج قرار دهید.
ArrayList extractedNodes = ExtractContent(startPara, endPara, true);

// محتوا را در یک سند جدید قرار دهید و آن را در دیسک ذخیره کنید.
Document dstDoc = GenerateDocument(doc, extractedNodes);
dstDoc.Save("output.docx");

متن را بین انواع مختلف گره ها در یک سند Word استخراج کنید

همچنین می توانید محتوا را بین انواع مختلف گره ها استخراج کنید. برای نمایش، بیایید محتوا را بین یک پاراگراف و یک جدول استخراج کنیم و آن را در یک سند جدید Word ذخیره کنیم. مراحل زیر برای انجام این عملیات آورده شده است.

  • سند Word را با استفاده از کلاس Document بارگیری کنید.
  • با استفاده از روش Document.FirstSection.Body.GetChild (NodeType، int، boolean)، ارجاع گره های شروع و پایان را به دو شیء دریافت کنید.
  • متد ExtractContent (startPara, endPara, True) را برای استخراج گره ها در یک شیء فراخوانی کنید.
  • برای ایجاد سند متشکل از محتوای استخراج شده، روش کمکی GenerateDocument (Document, extractedNodes) را فراخوانی کنید.
  • سند برگشتی را با استفاده از روش Document.Save (رشته) ذخیره کنید.

نمونه کد زیر نحوه استخراج متن بین یک پاراگراف و یک جدول در سی شارپ را نشان می دهد.

// سند Word را بارگیری کنید
Document doc = new Document("document.docx");

Paragraph startPara = (Paragraph)doc.LastSection.GetChild(NodeType.Paragraph, 2, true);
Table endTable = (Table)doc.LastSection.GetChild(NodeType.Table, 0, true);

// محتوای بین این گره ها را در سند استخراج کنید. این نشانگرها را در استخراج قرار دهید.
ArrayList extractedNodes = ExtractContent(startPara, endTable, true);

// محتوا را در یک سند جدید قرار دهید و آن را در دیسک ذخیره کنید.
Document dstDoc = GenerateDocument(doc, extractedNodes);
dstDoc.Save("output.docx");

استخراج متن بین پاراگراف ها بر اساس سبک

اکنون بیایید نحوه استخراج محتوا بین پاراگراف ها را بر اساس سبک ها بررسی کنیم. برای نمایش، ما بین اولین “هدینگ 1” و “هدینگ 3” اول در سند ورد، محتوایی را استخراج می کنیم. مراحل زیر نحوه دستیابی به این امر را در سی شارپ نشان می دهد.

  • ابتدا سند Word را با استفاده از کلاس Document بارگذاری کنید.
  • سپس با استفاده از روش کمکی ParagraphsByStyleName (Document, “Heading 1”) پاراگراف ها را در یک شی استخراج کنید.
  • با استفاده از روش کمکی ParagraphsByStyleName (Document, “Heading 3”) پاراگراف ها را به یک شی دیگر استخراج کنید.
  • متد ExtractContent (startPara, endPara, True) را فراخوانی کنید و عناصر اول را در هر دو آرایه پاراگراف به عنوان پارامترهای اول و دوم ارسال کنید.
  • برای ایجاد سند متشکل از محتوای استخراج شده، روش کمکی GenerateDocument (Document, extractedNodes) را فراخوانی کنید.
  • در نهایت سند برگشتی را با استفاده از روش Document.Save(string) ذخیره کنید.

نمونه کد زیر نحوه استخراج محتوا بین پاراگراف ها را بر اساس سبک ها نشان می دهد.

// سند Word را بارگیری کنید
Document doc = new Document("document.docx");

// فهرستی از پاراگراف ها را با استفاده از سبک های عنوان مربوطه جمع آوری کنید.
List<Paragraph> parasStyleHeading1 = ParagraphsByStyleName(doc, "Heading 1");
List<Paragraph> parasStyleHeading3 = ParagraphsByStyleName(doc, "Heading 3");

// از اولین نمونه پاراگراف ها با آن سبک ها استفاده کنید.
Node startPara1 = (Node)parasStyleHeading1[0];
Node endPara1 = (Node)parasStyleHeading3[0];

// محتوای بین این گره ها را در سند استخراج کنید. این نشانگرها را در استخراج وارد نکنید.
ArrayList extractedNodes = ExtractContent(startPara1, endPara1, false);

// محتوا را در یک سند جدید قرار دهید و آن را در دیسک ذخیره کنید.
Document dstDoc = GenerateDocument(doc, extractedNodes);
dstDoc.Save("output.docx");

ادامه مطلب

می توانید سناریوهای دیگر استخراج متن از اسناد Word را با استفاده از [این 5 مقاله مستند بررسی کنید.

یک مجوز API رایگان دریافت کنید

می توانید برای استفاده از Aspose.Words برای دات نت بدون محدودیت ارزیابی، مجوز موقت دریافت کنید.

نتیجه

در این مقاله نحوه استخراج متن از اسناد MS Word با استفاده از سی شارپ را آموختید. علاوه بر این، نحوه استخراج محتوا بین انواع مشابه یا متفاوت گره ها در یک سند Word را به صورت برنامه نویسی دیده اید. بنابراین، می توانید استخراج کننده متن MS Word خود را در سی شارپ بسازید. علاوه بر این، می‌توانید سایر ویژگی‌های Aspose.Words را برای دات‌نت با استفاده از اسناد کاوش کنید. در صورت داشتن هرگونه سوال، از طریق [تالار گفتمان8 ما را در جریان بگذارید.

همچنین ببینید

نکته: ممکن است بخواهید مبدل Aspose PowerPoint به Word را بررسی کنید، زیرا فرآیند تبدیل سند محبوب به Word را نشان می‌دهد.