Estrai testo da documenti MS Word in C#

L’estrazione del testo dai documenti di Word viene spesso eseguita in diversi scenari. Ad esempio, per analizzare il testo, per estrarre particolari sezioni di un documento e combinarle in un unico documento, e così via. In questo articolo imparerai come estrarre il testo dai documenti di Word a livello di codice in C#. Inoltre, tratteremo come estrarre il contenuto tra elementi specifici come paragrafi, tabelle, ecc. in modo dinamico.

Libreria C# per estrarre testo da documenti Word

Aspose.Words for .NET è una potente libreria che ti consente di creare documenti MS Word da zero. Inoltre, ti consente di manipolare i documenti Word esistenti per la crittografia, la conversione, l’estrazione di testo, ecc. Useremo questa libreria per estrarre il testo dai documenti Word DOCX o DOC. Puoi scaricare la DLL dell’API o installarla direttamente da NuGet utilizzando la console di gestione dei pacchetti.

PM> Install-Package Aspose.Words

Estrazione di testo nei documenti di Word utilizzando C#

Un documento MS Word è costituito da vari elementi che includono paragrafi, tabelle, immagini, ecc. Pertanto, i requisiti per l’estrazione del testo possono variare da uno scenario all’altro. Ad esempio, potrebbe essere necessario estrarre il testo tra paragrafi, segnalibri, commenti, ecc.

Ogni tipo di elemento in un documento di Word è rappresentato come un nodo. Pertanto, per elaborare un documento, dovrai giocare con i nodi. Quindi iniziamo e vediamo come estrarre testo da documenti Word in diversi scenari.

Estrai testo da un documento di Word in C#

In questa sezione, implementeremo un estrattore di testo C# per documenti Word e il flusso di lavoro dell’estrazione del testo sarebbe il seguente:

  • In primo luogo, definiremo i nodi che vogliamo includere nel processo di estrazione del testo.
  • Quindi, estrarremo il contenuto tra i nodi specificati (includendo o escludendo i nodi iniziali e finali).
  • Infine, utilizzeremo il clone dei nodi estratti, ad esempio per creare un nuovo documento Word composto da contenuto estratto.

Scriviamo ora un metodo chiamato ExtractContent a cui passeremo i nodi e alcuni altri parametri per eseguire l’estrazione del testo. Questo metodo analizzerà il documento e clonerà i nodi. Di seguito sono riportati i parametri che passeremo a questo metodo.

  1. StartNode ed EndNode rispettivamente come punti di inizio e fine per l’estrazione del contenuto. Questi possono essere nodi a livello di blocco (Paragraph, Table) o inline (ad es. Run, FieldStart, BookmarkStart ecc.).
    1. Per passare un campo devi passare l’oggetto FieldStart corrispondente.
    2. Per passare i segnalibri, è necessario passare i nodi BookmarkStart e BookmarkEnd.
    3. Per i commenti, è necessario utilizzare i nodi CommentRangeStart e CommentRangeEnd.
  2. IsInclusive definisce se i marker sono inclusi nell’estrazione o meno. Se questa opzione è impostata su false e vengono passati lo stesso nodo o nodi consecutivi, verrà restituito un elenco vuoto.

Di seguito è riportata l’implementazione completa del metodo ExtractContent che estrae il contenuto tra i nodi passati.

public static ArrayList ExtractContent(Node startNode, Node endNode, bool isInclusive)
{
    // Innanzitutto verificare che i nodi passati a questo metodo siano validi per l'uso.
    VerifyParameterNodes(startNode, endNode);

    // Crea un elenco per memorizzare i nodi estratti.
    ArrayList nodes = new ArrayList();

    // Conserva un record dei nodi originali passati a questo metodo in modo da poter dividere i nodi marker se necessario.
    Node originalStartNode = startNode;
    Node originalEndNode = endNode;

    // Estrarre il contenuto in base ai nodi a livello di blocco (paragrafi e tabelle). Attraversa i nodi principali per trovarli.
    // Divideremo il contenuto del primo e dell'ultimo nodo a seconda che i nodi marker siano in linea
    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;
    // Il nodo corrente che stiamo estraendo dal documento.
    Node currNode = startNode;

    // Inizia a estrarre il contenuto. Elabora tutti i nodi a livello di blocco e dividi in modo specifico il primo e l'ultimo nodo quando necessario, in modo da mantenere la formattazione del paragrafo.
    // Il metodo è leggermente più complesso di un normale estrattore in quanto è necessario tenere conto dell'estrazione utilizzando nodi, campi, segnalibri, ecc. Inline per renderlo davvero utile.
    while (isExtracting)
    {
        // Clona il nodo corrente e i suoi figli per ottenere una copia.
        Node cloneNode = currNode.Clone(true);
        isEndingNode = currNode.Equals(endNode);

        if ((isStartingNode || isEndingNode) && cloneNode.IsComposite)
        {
            // Abbiamo bisogno di elaborare ogni marcatore separatamente, quindi passalo invece a un metodo separato.
            if (isStartingNode)
            {
                ProcessMarker((CompositeNode)cloneNode, nodes, originalStartNode, isInclusive, isStartingNode, isEndingNode);
                isStartingNode = false;
            }

            // Conditional deve essere separato poiché i marker di inizio e fine a livello di blocco potrebbero essere lo stesso nodo.
            if (isEndingNode)
            {
                ProcessMarker((CompositeNode)cloneNode, nodes, originalEndNode, isInclusive, isStartingNode, isEndingNode);
                isExtracting = false;
            }
        }
        else
            // Il nodo non è un indicatore di inizio o fine, è sufficiente aggiungere la copia all'elenco.
            nodes.Add(cloneNode);

        // Passa al nodo successivo ed estrailo. Se il nodo successivo è nullo significa che il resto del contenuto si trova in una sezione diversa.
        if (currNode.NextSibling == null && isExtracting)
        {
            // Passa alla sezione successiva.
            Section nextSection = (Section)currNode.GetAncestor(NodeType.Section).NextSibling;
            currNode = nextSection.Body.FirstChild;
        }
        else
        {
            // Passa al nodo successivo nel corpo.
            currNode = currNode.NextSibling;
        }
    }

    // Restituisce i nodi tra gli indicatori di nodo.
    return nodes;
}

Alcuni metodi di supporto sono richiesti anche dal metodo ExtractContent per eseguire l’operazione di estrazione del testo, come indicato di seguito.

public static List<Paragraph> ParagraphsByStyleName(Document doc, string styleName)
{
    // Crea una matrice per raccogliere paragrafi dello stile specificato.
    List<Paragraph> paragraphsWithStyle = new List<Paragraph>();

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

    // Sfoglia tutti i paragrafi per trovare quelli con lo stile specificato.
    foreach (Paragraph paragraph in paragraphs)
    {
        if (paragraph.ParagraphFormat.Style.Name == styleName)
            paragraphsWithStyle.Add(paragraph);
    }

    return paragraphsWithStyle;
}
private static void VerifyParameterNodes(Node startNode, Node endNode)
{
    // L'ordine in cui vengono eseguiti questi controlli è importante.
    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");

    // Verificare che il nodo finale sia dopo il nodo iniziale nell'albero DOM
    // Per prima cosa controlla se si trovano in sezioni diverse, quindi se non stanno controllando la loro posizione nel corpo della stessa sezione in cui si trovano.
    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)
{
    // Verifica se il nodo è discendente di un nodo Paragrafo o Tabella e inoltre non è un paragrafo o una tabella è possibile un paragrafo all'interno di una classe di commento che sia discendente di un parafrafo.
    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)
{
    // Se abbiamo a che fare con un nodo a livello di blocco, controlla se deve essere incluso e aggiungilo all'elenco.
    if (!IsInline(node))
    {
        // Non aggiungere il nodo due volte se i marcatori sono lo stesso nodo
        if (!(isStartMarker && isEndMarker))
        {
            if (isInclusive)
                nodes.Add(cloneNode);
        }
        return;
    }

    // Se un marker è un nodo FieldStart, controlla se deve essere incluso o meno.
    // Assumiamo per semplicità che FieldStart e FieldEnd appaiano nello stesso paragrafo.
    if (node.NodeType == NodeType.FieldStart)
    {
        // Se l'indicatore è un nodo iniziale e non viene incluso, salta alla fine del campo.
        // Se il marker è un nodo finale e deve essere incluso, passare al campo finale in modo che il campo non venga rimosso.
        if ((isStartMarker && !isInclusive) || (!isStartMarker && isInclusive))
        {
            while (node.NextSibling != null && node.NodeType != NodeType.FieldEnd)
                node = node.NextSibling;

        }
    }

    // Se uno dei marcatori fa parte di un commento, per includere il commento stesso è necessario spostare il puntatore in avanti fino al Commento
    // Nodo trovato dopo il nodo CommentRangeEnd.
    if (node.NodeType == NodeType.CommentRangeEnd)
    {
        while (node.NextSibling != null && node.NodeType != NodeType.Comment)
            node = node.NextSibling;

    }

    // Trova il nodo corrispondente nel nostro nodo clonato per indice e restituiscilo.
    // Se il nodo iniziale e quello finale sono gli stessi, alcuni nodi figlio potrebbero essere già stati rimossi. Sottrarre il
    // Differenza per ottenere l'indice giusto.
    int indexDiff = node.ParentNode.ChildNodes.Count - cloneNode.ChildNodes.Count;

    // Conteggio dei nodi figlio identico.
    if (indexDiff == 0)
        node = cloneNode.ChildNodes[node.ParentNode.IndexOf(node)];
    else
        node = cloneNode.ChildNodes[node.ParentNode.IndexOf(node) - indexDiff];

    // Rimuovere i nodi fino a/dal marcatore.
    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();
    }

    // Dopo l'elaborazione, il nodo composito potrebbe diventare vuoto. Se ha non includerlo.
    if (!(isStartMarker && isEndMarker))
    {
        if (cloneNode.HasChildNodes)
            nodes.Add(cloneNode);
    }

}
public static Document GenerateDocument(Document srcDoc, ArrayList nodes)
{
    // Crea un documento vuoto.
    Document dstDoc = new Document();
    // Rimuovere il primo paragrafo dal documento vuoto.
    dstDoc.FirstSection.Body.RemoveAllChildren();

    // Importa ogni nodo dall'elenco nel nuovo documento. Mantieni la formattazione originale del nodo.
    NodeImporter importer = new NodeImporter(srcDoc, dstDoc, ImportFormatMode.KeepSourceFormatting);

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

    // Restituire il documento generato.
    return dstDoc;
}

Ora siamo pronti per utilizzare questi metodi ed estrarre il testo da un documento di Word.

Estrai il testo tra i paragrafi in un documento di Word

Vediamo come estrarre il contenuto tra due paragrafi in un documento Word DOCX. Di seguito sono riportati i passaggi per eseguire questa operazione in C#.

  • Innanzitutto, carica il documento di Word utilizzando la classe Document.
  • Ottieni il riferimento dei paragrafi iniziale e finale in due oggetti usando il metodo Document.FirstSection.Body.GetChild(NodeType.PARAGRAPH, int, boolean).
  • Chiama il metodo ExtractContent(startPara, endPara, True) per estrarre i nodi in un oggetto.
  • Chiama il metodo di supporto GenerateDocument(Document, extractNodes) per creare un documento costituito dal contenuto estratto.
  • Infine, salva il documento restituito utilizzando il metodo Document.Save(string).

Nell’esempio di codice seguente viene illustrato come estrarre il testo tra il settimo e l’undicesimo paragrafo in un documento di Word in C#.

// Carica documento Word
Document doc = new Document("document.docx");

// Raccogli i nodi (il metodo GetChild utilizza un indice basato su 0)
Paragraph startPara = (Paragraph)doc.FirstSection.Body.GetChild(NodeType.Paragraph, 6, true);
Paragraph endPara = (Paragraph)doc.FirstSection.Body.GetChild(NodeType.Paragraph, 10, true);

// Estrarre il contenuto tra questi nodi nel documento. Includere questi marcatori nell'estrazione.
ArrayList extractedNodes = ExtractContent(startPara, endPara, true);

// Inserisci il contenuto in un nuovo documento e salvalo su disco.
Document dstDoc = GenerateDocument(doc, extractedNodes);
dstDoc.Save("output.docx");

Estrai testo tra diversi tipi di nodi in un documento di Word

Puoi anche estrarre contenuto tra diversi tipi di nodi. A scopo dimostrativo, estraiamo il contenuto tra un paragrafo e una tabella e lo salviamo in un nuovo documento di Word. Di seguito sono riportati i passaggi per eseguire questa operazione.

  • Carica il documento di Word usando la classe Document.
  • Ottieni il riferimento dei nodi iniziali e finali in due oggetti usando il metodo Document.FirstSection.Body.GetChild(NodeType, int, boolean).
  • Chiama il metodo ExtractContent(startPara, endPara, True) per estrarre i nodi in un oggetto.
  • Chiama il metodo di supporto GenerateDocument(Document, extractNodes) per creare un documento costituito dal contenuto estratto.
  • Salva il documento restituito utilizzando il metodo Document.Save(string).

Nell’esempio di codice seguente viene illustrato come estrarre il testo tra un paragrafo e una tabella in C#.

// Carica documento 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);

// Estrarre il contenuto tra questi nodi nel documento. Includere questi marcatori nell'estrazione.
ArrayList extractedNodes = ExtractContent(startPara, endTable, true);

// Inserisci il contenuto in un nuovo documento e salvalo su disco.
Document dstDoc = GenerateDocument(doc, extractedNodes);
dstDoc.Save("output.docx");

Estrai il testo tra i paragrafi in base agli stili

Diamo ora un’occhiata a come estrarre il contenuto tra i paragrafi in base agli stili. A scopo dimostrativo, estrarremo il contenuto tra il primo “Titolo 1” e il primo “Titolo 3” nel documento di Word. I passaggi seguenti illustrano come ottenere ciò in C#.

  • Innanzitutto, carica il documento di Word utilizzando la classe Document.
  • Quindi, estrai i paragrafi in un oggetto usando il metodo di supporto ParagraphsByStyleName(Document, “Heading 1”).
  • Estrarre i paragrafi in un altro oggetto utilizzando il metodo di supporto ParagraphsByStyleName(Document, “Heading 3”).
  • Chiama il metodo ExtractContent(startPara, endPara, True) e passa i primi elementi in entrambe le matrici di paragrafi come primo e secondo parametro.
  • Chiama il metodo di supporto GenerateDocument(Document, extractNodes) per creare un documento costituito dal contenuto estratto.
  • Infine, salva il documento restituito utilizzando il metodo Document.Save(string).

Nell’esempio di codice seguente viene illustrato come estrarre il contenuto tra i paragrafi in base agli stili.

// Carica documento Word
Document doc = new Document("document.docx");

// Raccogli un elenco dei paragrafi utilizzando i rispettivi stili di intestazione.
List<Paragraph> parasStyleHeading1 = ParagraphsByStyleName(doc, "Heading 1");
List<Paragraph> parasStyleHeading3 = ParagraphsByStyleName(doc, "Heading 3");

// Usa la prima istanza dei paragrafi con quegli stili.
Node startPara1 = (Node)parasStyleHeading1[0];
Node endPara1 = (Node)parasStyleHeading3[0];

// Estrarre il contenuto tra questi nodi nel documento. Non includere questi marcatori nell'estrazione.
ArrayList extractedNodes = ExtractContent(startPara1, endPara1, false);

// Inserisci il contenuto in un nuovo documento e salvalo su disco.
Document dstDoc = GenerateDocument(doc, extractedNodes);
dstDoc.Save("output.docx");

Leggi di più

Puoi esplorare altri scenari di estrazione di testo da documenti Word usando questo articolo della documentazione.

Ottieni una licenza API gratuita

Puoi ottenere una licenza temporanea per utilizzare Aspose.Words per .NET senza limitazioni di valutazione.

Conclusione

In questo articolo, hai imparato come estrarre testo da documenti MS Word usando C#. Inoltre, hai visto come estrarre il contenuto tra tipi simili o diversi di nodi in un documento di Word a livello di codice. Pertanto, puoi creare il tuo estrattore di testo MS Word in C#. Inoltre, puoi esplorare altre funzionalità di Aspose.Words per .NET usando la documentazione. In caso di domande, non esitare a farcelo sapere tramite il nostro forum.

Guarda anche

Suggerimento: potresti voler controllare Aspose PowerPoint to Word Converter perché mostra il popolare processo di conversione da presentazione a documento Word.