חלץ טקסט ממסמכי MS Word ב-C#

חילוץ טקסט ממסמכי Word מבוצע לרוב בתרחישים שונים. לדוגמה, לנתח את הטקסט, לחלץ חלקים מסוימים של מסמך ולשלב אותם למסמך בודד, וכן הלאה. במאמר זה תלמדו כיצד לחלץ טקסט ממסמכי Word ב-C#. יתר על כן, נסקור כיצד לחלץ תוכן בין אלמנטים ספציפיים של מסמך Word כגון פסקאות, טבלאות וכו'.

כיצד לחלץ טקסט מ-Word ב-C#

לחילוץ טקסט ממסמכי Word, נשתמש ב-Aspose.Words for .NET. זוהי ספריה רבת עוצמה המאפשרת לך ליצור ולעבד מסמכי MS Word. אתה יכול בקלות לקבל את [רישיון החינמי] שלו (#Get-a-Free-API-License) ולחלץ טקסט ממסמכי Word ללא כל הגבלה.

הורד את ה-DLL או התקן את הספרייה ישירות מ-NuGet באמצעות קונסולת מנהל החבילות.

PM> Install-Package Aspose.Words

חילוץ טקסט ממסמכי וורד

מסמך MS Word מורכב מאלמנטים שונים הכוללים פסקאות, טבלאות, תמונות וכו’. לכן, הדרישות של חילוץ טקסט עשויות להשתנות מתרחיש אחד למשנהו. לדוגמה, ייתכן שיהיה עליך לחלץ טקסט בין פסקאות, סימניות, הערות וכו'.

כל סוג של רכיב במסמך Word מיוצג כצומת. לכן, כדי לעבד מסמך, תצטרך לשחק עם הצמתים. אז בואו נתחיל ונראה כיצד לחלץ טקסט ממסמכי Word בתרחישים שונים.

קבל טקסט מ- Word DOCX ב-C#

בסעיף זה, אנו הולכים ליישם מחלץ טקסט C# עבור מסמכי 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
            // צומת אינו סמן התחלה או סיום, פשוט הוסף את העותק לרשימה.
            nodes.Add(cloneNode);

        // עבור לצומת הבא וחלץ אותו. אם הצומת הבא הוא null זה אומר ששאר התוכן נמצא בקטע אחר.
        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. להלן השלבים לביצוע פעולה זו ב-C#.

  • ראשית, טען את מסמך Word באמצעות מחלקה Document.
  • קבל הפניה של פסקאות ההתחלה והסיום לשני אובייקטים באמצעות שיטת Document.FirstSection.Body.GetChild(NodeType.PARAGRAPH, int, boolean).
  • קרא לשיטת ExtractContent(startPara, endPara, True) כדי לחלץ את הצמתים לאובייקט.
  • קרא לשיטת העזר GenerateDocument(Document, extractedNodes) כדי ליצור מסמך המורכב מהתוכן שחולץ.
  • לבסוף, שמור את המסמך המוחזר באמצעות שיטת Document.Save(מחרוזת).

דוגמת הקוד הבאה מראה כיצד לחלץ טקסט בין הפסקה השביעית ל-11 במסמך Word ב-C#.

// טען מסמך וורד
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(מחרוזת).

דוגמת הקוד הבאה מראה כיצד לחלץ טקסט בין פסקה לטבלה ב-C#.

// טען מסמך וורד
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. השלבים הבאים מדגימים כיצד להשיג זאת ב-C#.

  • ראשית, טען את מסמך Word באמצעות מחלקה Document.
  • לאחר מכן, חלץ פסקאות לאובייקט באמצעות שיטת העזר ParagraphsByStyleName(Document, “Heading 1”).
  • חלץ פסקאות לאובייקט אחר באמצעות שיטת העזר ParagraphsByStyleName(Document, “Heading 3”).
  • קראו לשיטת ExtractContent(startPara, endPara, True) והעבירו את האלמנטים הראשונים בשני מערכי הפסקה כפרמטר ראשון ושני.
  • קרא לשיטת העזר GenerateDocument(Document, extractedNodes) כדי ליצור מסמך המורכב מהתוכן שחולץ.
  • לבסוף, שמור את המסמך המוחזר באמצעות שיטת Document.Save(מחרוזת).

דוגמת הקוד הבאה מראה כיצד לחלץ תוכן בין פסקאות בהתבסס על סגנונות.

// טען מסמך וורד
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 באמצעות מאמר התיעוד זה.

מחלץ טקסט MS Word בחינם

אתה יכול לקבל [רישיון זמני] בחינם6 לחילוץ טקסט ללא מגבלות הערכה.

סיכום

במאמר זה, למדת כיצד לחלץ טקסט ממסמכי MS Word באמצעות C#. יתר על כן, ראית כיצד לחלץ תוכן בין סוגים דומים או שונים של צמתים במסמך Word באופן תכנותי. לפיכך, אתה יכול לבנות מחלץ טקסט משלך של MS Word ב-C#. חוץ מזה, אתה יכול לחקור תכונות אחרות של Aspose.Words עבור .NET באמצעות תיעוד. למקרה שיש לך שאלות כלשהן, אל תהסס ליידע אותנו דרך הפורום שלנו.

ראה גם

טיפ: ייתכן שתרצה לסמן את Aspose PowerPoint to Word Converter מכיוון שהוא מדגים את תהליך ההמרה הפופולרי של מצגת ל-Word.