แยกข้อความจากเอกสาร MS Word ใน C#

การแยกข้อความจากเอกสาร Word มักดำเนินการในสถานการณ์ต่างๆ ตัวอย่างเช่น เพื่อวิเคราะห์ข้อความ เพื่อแยกส่วนเฉพาะของเอกสารและรวมเข้าด้วยกันเป็นเอกสารเดียว เป็นต้น ในบทความนี้ คุณจะได้เรียนรู้วิธีการแยกข้อความจากเอกสาร Word โดยทางโปรแกรมใน C# นอกจากนี้ เราจะกล่าวถึงวิธีการแยกเนื้อหาระหว่างองค์ประกอบเฉพาะ เช่น ย่อหน้า ตาราง ฯลฯ แบบไดนามิก

C# Library เพื่อดึงข้อความจากเอกสาร Word

Aspose.Words for .NET เป็นไลบรารีอันทรงพลังที่ช่วยให้คุณสามารถสร้างเอกสาร MS Word ได้ตั้งแต่เริ่มต้น นอกจากนี้ ยังให้คุณจัดการเอกสาร Word ที่มีอยู่สำหรับการเข้ารหัส การแปลง การดึงข้อความ ฯลฯ เราจะใช้ไลบรารีนี้เพื่อแยกข้อความจากเอกสาร Word DOCX หรือ DOC คุณสามารถ ดาวน์โหลด DLL ของ API หรือติดตั้งโดยตรงจาก NuGet โดยใช้คอนโซลตัวจัดการแพ็คเกจ

PM> Install-Package Aspose.Words

การแยกข้อความในเอกสาร Word โดยใช้ C#

เอกสาร MS Word ประกอบด้วยองค์ประกอบต่างๆ ซึ่งรวมถึงย่อหน้า ตาราง รูปภาพ เป็นต้น ดังนั้นข้อกำหนดในการแยกข้อความจึงอาจแตกต่างกันไปในแต่ละสถานการณ์ ตัวอย่างเช่น คุณอาจต้องแยกข้อความระหว่างย่อหน้า ที่คั่นหน้า ความคิดเห็น ฯลฯ

องค์ประกอบแต่ละประเภทในเอกสาร Word จะแสดงเป็นโหนด ดังนั้นในการประมวลผลเอกสาร คุณจะต้องเล่นกับโหนด เรามาเริ่มกันและดูวิธีแยกข้อความจากเอกสาร Word ในสถานการณ์ต่างๆ

แยกข้อความจากเอกสาร Word ใน 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)
{
    // ทดสอบว่าโหนดเป็น desendant ของโหนดย่อหน้าหรือตาราง และไม่ใช่ย่อหน้าหรือตารางในย่อหน้าในคลาสความคิดเห็นซึ่งอยู่ถัดจากย่อหน้าที่เป็นไปได้
    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(string)

ตัวอย่างโค้ดต่อไปนี้แสดงวิธีการแยกข้อความระหว่างย่อหน้าที่ 7 และ 11 ในเอกสาร Word ใน C#

// โหลดเอกสาร 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(string)

ตัวอย่างโค้ดต่อไปนี้แสดงวิธีการแยกข้อความระหว่างย่อหน้าและตารางใน C#

// โหลดเอกสาร 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 ขั้นตอนต่อไปนี้แสดงให้เห็นถึงวิธีการบรรลุสิ่งนี้ใน C#

  • ขั้นแรก ให้โหลดเอกสาร 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 โดยใช้บทความคู่มือ this

รับใบอนุญาต API ฟรี

คุณสามารถรับ ใบอนุญาตชั่วคราว เพื่อใช้ Aspose.Words for .NET โดยไม่มีข้อจำกัดในการประเมิน

บทสรุป

ในบทความนี้ คุณได้เรียนรู้วิธีแยกข้อความจากเอกสาร MS Word โดยใช้ C# นอกจากนี้ คุณได้เห็นวิธีแยกเนื้อหาระหว่างโหนดประเภทเดียวกันหรือต่างชนิดกันในเอกสาร Word โดยทางโปรแกรม ดังนั้น คุณสามารถสร้างตัวแยกข้อความ MS Word ของคุณเองใน C# นอกจากนี้ คุณสามารถสำรวจคุณลักษณะอื่นๆ ของ Aspose.Words for .NET โดยใช้ เอกสารประกอบ ในกรณีที่คุณมีคำถามใดๆ โปรดแจ้งให้เราทราบผ่านทาง ฟอรัม ของเรา

ดูสิ่งนี้ด้วย

เคล็ดลับ: คุณอาจต้องกาเครื่องหมาย Aspose PowerPoint to Word Converter เนื่องจากจะสาธิตกระบวนการแปลงเอกสารงานนำเสนอเป็น Word ที่ได้รับความนิยม